您的位置:首页 > 移动开发 > Objective-C

关于类成员的初始化顺序问题

2006-11-22 15:11 369 查看
1.关于类成员的初始化顺序问题
        条款13的标题是:initialization list中的members初始化次序应该和其在class内的声明次序相同。
        我不知道大家在用C++开发的时候有没有注意过这个问题,反正我是从来没有往这方面想过!
        下面来看例子:

class CMyIntArray
{
public:
    CMyIntArray(int lowBound, int highBound);
    size_t GetArraySize() const { return data.size(); };
private:
    std::vector<int> data;
    size_t size;
    int lBound, hBound;
};

CMyIntArray::CMyIntArray(int lowBound, int highBound)
          : size(highBound - lowBound +1)
          , lBound(lowBound)
          , hBound(highBound)
          , data(size)
{
}

        如果没有看过这本书,没有任何提示,我想很多搞C++开发的人看不出这简单几行代码有什么问题(当然,编译肯定是不会有问题的)。
        可是,如果程序中有类似于下面的代码,问题就会在程序运行的时候爆发出来:

CMyIntArray oTest(5, 13);
cout << oTest.GetArraySize() << endl;

        而且你就算在debug状态下进行跟踪也很难发现问题的根源所在!
        问题处在什么地方呢?就在于成员变量的声明次序上!上面变量声明的次序改为:

size_t size;
int lBound, hBound;
std::vector<int> data;

        问题就可以迎刃而解!
        惊奇吗?为什么会这样?书中给出了答案:class members系以它们在class内的声明次序来初始化;和它们在member initialization list中出现的次序完全无关。
        上面代码的错误根源是:data在得到size之前就已经初始化了,也就是说data的大小是不确定的,调用它的size方法当然会出错!

2.关于“切割问题”
        下面是书中的一段话(条款22中):
        当一个derived class object被交出去当作一个base class object时,它原本所以“成为一个derived class object”的所有特征,都会被切除(slicing)掉,只留下内部一个base class object。
         下面是我写的小例子:

class CBase
{
public:
    virtual void Test() const { cout << "Output from CBase!" << endl; };
};

class CDerived : public CBase
{
public:
    virtual void Test() const { cout << "Output from CDerived!" << endl; };
};

下面是两个函数:

void Test1(CBase test)
{
    test.Test();
}

void Test2(const CBase& test)
{
    test.Test();
}

        用下面的代码分别调用函数Test1和Test2:

CDerived oTest;
Test1(oTest);
Test2(oTest);

        问题是:调用函数Test1和函数Test2分别输出什么?
        正确答案是:

Output from CBase!
Output from CDerived!

        惊奇吗?一点儿也不惊奇!书中已经给出了为什么会这样:以by reference的方式传递参数,有另一个优点:可避免所谓的“切割(slicing)问题”。

3.关于非虚拟函数的静态绑定和虚拟函数的动态绑定
        下面是例子:

class CBase
{
public:
    virtual void Test() const { cout << "Output from CBase!" << endl; };
};

class CDerived : public CBase
{
public:
    void Test() const { cout << "Output from CDerived!" << endl; };
};

        现在有下面的代码调用:

CDerived  d;

CBase *pB = &d;
pB->Test();

CDerived  *pD = &d;
pD->Test();

        输出是:

Output from CDerived!
Output from CDerived!

        这个大家应该都知道为什么。可是,如果把CBase的Test方法前的virtual去掉,结果就变了!输出就是:

Output from CBase!
Output from CDerived!

        这时,有些人就会奇怪了,pB和pD指向的都是d,为什么会这样输出呢?
        答案就是:非虚拟函数是静态绑定的,虚拟函数是动态绑定的。
        书中的条款37给了大家忠告:绝对不要重新定义继承而来的非虚拟函数。

4.关于缺省值的静态绑定
        还是看例子:

class CBase
{
public:
    virtual void Test(int iTest = 0) const  = 0;
};

class CDerived : public CBase
{
public:
    void Test(int iTest = 1) const { cout << iTest << endl; };
};

        下面是调用代码:

CBase *p = new CDerived;
p->Test();

        我们的本意是想输出1,可是结果却是输出0!
        不用惊奇,书中给出了答案:虚拟函数系动态绑定(dynamically bound),而缺省参数值却是静态绑定(statically bound)。
        而且,书中的条款38也给了大家忠告:绝对不要重新定义继承而来的缺省参数。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息