深入探索c++对象模型(九、成员变量的绑定)

这一篇我们开启《深入探索c++对象模型》的第3章 Data语义学,第3章开头介绍了对象的大小,这个我们在第一篇的时候已经讲过了,不过第一篇介绍的比较少,只是简单的描述了几种情况下类对象的大小,忘记的可以回去看下第一篇,我也忘记了,刚刚也回去看了下,哈哈哈。

9.1 成员变量绑定时机

我们现在写类变量,经常是在后面写类变量,类方法经常写前面比如:

// 类中再定义一个x
class A
{
public:
    float X() const {return x;}   // 访问的是类中的x
    float GX() const {return ::x;}	// 这个访问的是全局的x

private:
    float x;
};

这种写法是不是习以为常,下面我们就来谈论谈论一个细思极恐的东西。

9.1.1 类中定义函数访问类变量

我们来看看例子:

#include <iostream>

using namespace std; 

//定义一个全局的变量x
float x = 11;

// 可以提到这里试试

// 类中再定义一个x
class A
{
public:
    float X() const {return x;}   // 访问的是类中的x
    float GX() const {return ::x;}	// 这个访问的是全局的x

private:
    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,需要使用::

9.1.2 类外定义函数访问类变量

我们在尝试一下,把函数体放在类外面试试:

float x = 11;
// 类中再定义一个x
class A
{
public:
    float X() const;
    float GX() const;

private:
    float x;
};

float A::X() const {return x;}		// 访问的是类中的x

float A::GX() const {return ::x;}	// 这个访问的是全局的x

这样结果也是一样的。

刚刚想到一种是把函数移动到类的前面,这样就编译报错,因为类A还没加载,找不到类A。

9.1.3 内联函数访问类变量

还有一个内联函数:

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++编译器的事,这里就不提,都是旧时代的东西了,我们现在是新时代了,哈哈哈。

9.2 使用typedef定义变量绑定时机

既然上面是c++后来的标准改变了,那我们就试试使用typedef的方式定义下变量。

9.2.1 类中定义函数访问

我们先来看看类中定义函数的情况:

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;} 

9.2.2 类外定义函数访问

类中定义有问题,那我们看看类外的,虽然我们直接告诉我们这个也会出错,但还是要分析分析。

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类型
 
//类外内存,因为类已经定义了,所以这个是float
 void A::mumble(float val)      // 这个也报错,没有定义这个类型,这个length是float类型
{
    _val = val;
}

所以这个报错是类中没有定义这个函数。

9.3.3 正确玩法

经过上面的分析,对于成员函数参数:是在编译器第一次遇到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的话,记得提前。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值