c++的引用一直都是一个很重要的概念,而且使用的场合很多,比如通过引用减少不必要的临时对象拷贝,或者通过传递引用作为函数的返回值。
int get_stm(int& stm); 这里的stm就是需要get的值,而函数本身的返回值则作为错误判断的依据,例如返回0则正确,非0值则表示错误等。
前面说的这些都是很常规的用法,也不存在什么误区。最近在设计一个内存模型时有这样的一个需求:
class Table
{
int table_id;
Row* row_array;
};
class Row
{
int table_id;
int column_num;
...
...
}
在这两个类中,第一个类描述一个数据库的表,而第二个类则描述了一个表中的某一行的信息,这里面存在着数据的冗余,table_id。因为一张表的每一行的table_id都是相同的,而如果想要在每一行中省略掉table_id的内存开销,有几种方式。
1 将table_id设定为static
2 将table_id改为一个引用
先说第一种方式,看似可以解决问题,但实际中,由于涉及到多张表,每张表的table_id必定不同,而如果将Row的table_id设定为static,就会出现每张表的每行数据的table_id是相同的,这样显然是错误的。如果改为一个static数组,似乎可以解决问题,但又过于复杂。
第二种方式,利用引用,代码大致是这样的:
class Row
{
const int& table_id;
...
public:
Row(const int& t_id) : table_id(t_id)
{}
};
看上去很完美,既节省空间,而且关系清晰,不会出现问题。
在这种方式中,我们做了一个假定:c++中的引用不会占用额外的内存。
从引用的原理上看确实是这样,它是一个别名,作为一个并不存在的东西指向实际的变量...
但实际上呢?
如果你在gcc中测试这样的一段代码:
class A
{
int& a;
int& b;
int& c;
int& d;
int e;
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
就会发现输出的结果是36 !!!! (64位环境)
36 = 32 + 4; 也就是4个指针和一个int。
换句话说,引用并不像它看上去的那么美好,其实不过是一层指针的封装.....
一直以来都觉得引用是一种类似作用域的扩展,也就是把变量原本的作用域,扩展到了需要的地方,而实现的方式,大致是在链接的时候,将所引用的符号变量替换为实际的地址.....虽然也听过引用的实现和指针类似,但是还一直觉得是原理类似,而不是实现。
但现实真的很残酷。
在gcc,clang,vc等等编译器中,都是使用一个简单的指针替换引用,大概类似这样:
int b;
int& c = b;
c = 3;
//实际实现时
int b;
int* c = &b;
*c = 3;
注:就算是全局空间的引用,也需要占用一个指针的内存,编译器并没有对其做任何优化。
深究其原因,是因为c++标准中并没有定义引用该如何实现,而大家就不约而同的选择了相对简单的实现方式....
结论:
1 引用并不会节省内存,开销相当于一个指针
2 所有使用引用的地方都可以用指针替代,反之则不然(无法实现空引用)
(原文时间2014-1-21)