这一篇我们开启《深入探索c++对象模型》的第3章 Data语义学,第3章开头介绍了对象的大小,这个我们在第一篇的时候已经讲过了,不过第一篇介绍的比较少,只是简单的描述了几种情况下类对象的大小,忘记的可以回去看下第一篇,我也忘记了,刚刚也回去看了下,哈哈哈。
我们现在写类变量,经常是在后面写类变量,类方法经常写前面比如:
// 类中再定义一个x
class A
{
public:float X() const {return x;} // 访问的是类中的xfloat GX() const {return ::x;} // 这个访问的是全局的xprivate:float x;
};
这种写法是不是习以为常,下面我们就来谈论谈论一个细思极恐的东西。
我们来看看例子:
#include using namespace std; //定义一个全局的变量x
float x = 11;// 可以提到这里试试// 类中再定义一个x
class A
{
public:float X() const {return x;} // 访问的是类中的xfloat GX() const {return ::x;} // 这个访问的是全局的xprivate:float x;
};int main(int argc, char **argv)
{A a;cout << "x = " << a.X() << " GX = " << a.GX() << endl;return 0;
}
这个例子专门在全局部分添加了一个float x = 11;以这个来测试一下编译器到底是找那个x。
我们编译,执行一下看看:
[mmog@localhost 09]$ ./9_1
x = -8.02677e-35 GX = 11
A::X() 访问的哪个x,应该都知道吧,就是访问类中的x,编译器对成员函数的解析,是整个类A定义完成之后,才进行的。
A::GX()访问的就是全局的x,要想访问全局的x,需要使用::
我们在尝试一下,把函数体放在类外面试试:
float x = 11;
// 类中再定义一个x
class A
{
public:float X() const;float GX() const;private:float x;
};float A::X() const {return x;} // 访问的是类中的xfloat A::GX() const {return ::x;} // 这个访问的是全局的x
这样结果也是一样的。
刚刚想到一种是把函数移动到类的前面,这样就编译报错,因为类A还没加载,找不到类A。
还有一个内联函数:
float x = 11;class A
{
public:float X() const;float GX() const;inline float XX() const {return x;}private:float x;
};
内联函数也是在类A定义完之后,才进行解析的。
float x = 11;class A
{
public:float X() const;float GX() const;float XX() const;private:float x;
};float A::X() const {return x;}float A::GX() const {return ::x;}inline float A::XX() const {return x;}
在外面定义也是如此。
侯捷老师说的以前的c++编译器的事,这里就不提,都是旧时代的东西了,我们现在是新时代了,哈哈哈。
既然上面是c++后来的标准改变了,那我们就试试使用typedef的方式定义下变量。
我们先来看看类中定义函数的情况:
int _val = 0;
//定义一个全局的变量x
typedef int length;// 类中再定义一个x
class A
{
public:void mumble(length val) {_val = val;} // 编译出错,_val是float类型,val是int类型,两个类型不相等length mumble() {return val;} // 这个返回的是int 类型private:typedef float length;length _val; // 这个是float 类型};
这样编译会出错,根据就近原则,类的函数应该是样的:
void mumble(int val) {_val = val;} // _val是float 所以出错
int mumble() {return val;}
类中定义有问题,那我们看看类外的,虽然我们直接告诉我们这个也会出错,但还是要分析分析。
int _val = 0;
//定义一个全局的变量x
typedef int length;// 类中再定义一个x
class A
{
public:void mumble(length val); // 这个是int类型length mumble() {return val;} // 这个返回的是int 类型private:typedef float length;length _val; // 这个是float 类型};void A::mumble(length val) // 这个也报错,没有定义这个类型,这个length是float类型
{_val = val;
}void mumble(length val) // 普通函数是正确的,这是找全局的length,是int类型
{int _val = val;
}
很明显这种写法也出错了,不过报错的是另一个问题,我们来分析一下,
根据就近原则和类作用域:
// 类中函数,提取出来分析
void mumble(int val); // 这个是int类型//类外内存,因为类已经定义了,所以这个是floatvoid A::mumble(float val) // 这个也报错,没有定义这个类型,这个length是float类型
{_val = val;
}
所以这个报错是类中没有定义这个函数。
经过上面的分析,对于成员函数参数:是在编译器第一次遇到length的时候被决定,所以length第一次被遇到编译器只看到了typedef int length; 所以函数都被翻译成int。
为了类中尽早看到类类型length,所以typedef定义要被提前,这样就不会出现奇奇怪怪的问题了。
int _val = 0;
//定义一个全局的变量x
typedef int length;// 类中再定义一个x
class A
{typedef float length; // 提前
public:void mumble(length val); // 这个是float类型length mumble() {return _val;} // 这个返回的是float 类型private:length _val; // 这个是float 类型};void A::mumble(length val)
{_val = val;
}void mumble(length val) // 普通函数是正确的,这是找全局的length,是int类型
{int _val = val;
}
提前能解决很多问题,下次注意,如果用typedef的话,记得提前。