C++对象模型学习笔记

1. 全局变量是如何初始化的

//global var
A a;

int main()
{
    cout<<a<<endl;
    return 0;  
} 

  如上述例子,全局变量a是在main()函数之前便被初始化的,但是它是如何被初始化的呢?答案是所谓的静态初始化

2. C++对C进行封装的布局成本

  C++在布局和存取时间上主要的额外负担是由virtual 机制引起的,包括:

      virtual function机制:用于支持有效率的“执行期绑定”

      virtual base class:用以实现“多次出现在继承体系中的base class ,有一个单一而被共享的实体”

  此外,还有一些多重继承下的额外负担,发生在“一个derived class 和其第二或后继之 base class 的转换”之间。然而,一般而言,并没有什么天生的理由说C++程序一定比C兄弟庞大或迟缓。

3. 父类与子类的指针有什么不同?

  看下面例子:

class ZooAnimal
{
public:
    ZooAnimal(): name("")
    {
        cout<<"ZooAnimal::ZooAnimal()"<<endl;
    }

    ZooAnimal(string str): name(str)
    {
        cout<<"ZooAnimal::ZooAnimal(string)"<<endl;
    }

    virtual ~ZooAnimal()
    {
        cout<<"ZooAnimal::~ZooAnimal()"<<endl;
    }

    virtual void rotate()
    {
        cout<<"ZooAnimal::rotate()"<<endl;
    }


protected:
    int loc;
    string name;
};



class Bear: public ZooAnimal
{
public:
    Bear()
    {
        cout<<"Bear::Bear()"<<endl;
    }

    Bear(string str): ZooAnimal(str)
    {
        cout<<"Bear::Bear(string)"<<endl;
    }

    ~Bear()
    {
        cout<<"Bear::~Bear()"<<endl;
    }

    void rotate()
    {
        cout<<"Bear::rotate()"<<endl;
    }

    virtual void dance()
    {
        cout<<"Bear::dance()"<<endl;
    }


protected:
    enum Dances{DAN, SLEEP};

    Dances dances_known;
    int cell_block;
};


int main()
{
    cout<<"sizeof(ZooAnimal):"<<sizeof(ZooAnimal)<<endl;
    cout<<"sizeof(Bear):"<<sizeof(Bear)<<endl;

    Bear b("Yogi");
    Bear *pb = &b;
    ZooAnimal *pbr = &b;
    
    return 0;
}

  指针pb 和 pbr 有什么不同呢?它们都指向Bear对象b的第一个字节,其间的差别是,pb所涵盖的地址包含了整个Bear 对象,而 pbr 所涵盖的地址只包含Bear 对象中的ZooAnimal 部分!

 4. 自定义类什么时候自动生成默认构造函数?

  答案:在编译器需要的时候,什么时候需要呢,有如下四种情况:

  合成出来的构造函数只执行编译器所需的行动。也就是说,即使有需要为自定义类合成一个默认构造函数,那个构造函数也不会将类的数据成员初始化为0。

  因此,类的设计者必须提供一个明显的默认构造函数,将数据成员初始化!

  全局对象的内存保证会在程序激活的时候被清零,本地变量对象配置于程序的堆栈中,Heap 对象配置与自由空间中,都不一定被清零。

 

  1. 如果一个类 B 没有任何构造函数,但它内含有一个数据成员A a,而 A 有默认构造函数,那么编译器需要为这个类 B 合成一个默认构造函数。

class Foo
{
public:
    Foo() {...}
    Foo(int) {...}
};


class Bar 
{
public:
    Foo f;
    char *str;
};

  如上例,编译器会自动为类Bar合成一个默认构造函数。被合成的Bar default constructor 内含必要的代码,能够调用 class Foo 的default constructor 来处理数据成员f的初始化,但它并不产生任何代码来初始化Bar::str。被合成的default constructor 看起来可能像这样:

inline Bar::Bar()
{
    //C++伪码
    f.Foo::Foo();
}

  2. 如果该类继承自一个带有默认构造函数的基类,那么编译器也会自动合成一个默认构造函数;

  3. 带有一个Virtual Function 的类,这时,编译器也会为之合成一个默认构造函数。

  4. 带有一个Virtual Base Class 的类,如下例子所示:

class X
{
public: 
    int i;
};

class A: public virtual X
{
...
}



class B: public virtual X
{
...
}


class C: public A, public B
{
...
}

  那么,编译器将会为类C合成一个默认构造函数!

 

  注意:C++新手一般有两个常见的误解:

  1. 任何class 如果没有定义default constructor,就会被合成一个。

  2. 合成的default constructor 会初始化数据成员。

5. 复制构造函数的相关问题

  类的复制构造函数的一般模式为:

X::X(const X& x)
{
    ....
}

5.1 什么情形会调用复制构造函数

  有三种情况,分别如下:

  1) 赋值操作,例如:

class X { ...};
X a;
X b = a;   //copy constructor

  2) 对函数传递类参数时,例如:

void foo(X x)
{
    ...
}

X a;
foo(a);    //copy constructor

  3) 当函数传回一个类对象时,这个时候可能编译器做了优化,因此,可能copy constructor 不被调用。例如:

X foo()
{
    X a;
    return a;
}

X b = foo();  //copy constructor

 

5.2 当类设计者没有定义类的复制构造函数时如何初始化类数据成员?

  其内部是以所谓的默认逐个成员初始化的方法完成的。也就是把每一个内建的或派生的数据成员的值从某个对象拷贝一份到另一个对象身上。 

5.3 默认构造函数和复制构造函数在必要的时候才由编译器产生出来。

 

posted @ 2014-12-02 19:44  wiessharling  阅读(183)  评论(0编辑  收藏  举报