C++对象模型之二 构造语句
构造函数
你是不是这样认为:
1. 任何类如果没有定义默认构造函数,编译器就会合成一个;
2. 编译器合成的会明确设定类中每个数据成员的默认值;
事实并非如此的,只有C++编译器需要的时候才会合成个默认构造函数。
并且在4种情况下会那么做。
第一种情况:类中带有对象成员
class A{……};
class B
{
private:
A a; //对象成员
int x;
}
合成的构造函数
B::B ( )
{
a.A::A();
}
假如你定义了个构造函数
A::A ( )
{
x=0;
}
编译器会追加代码到里面去
A::A ()
{
a.A::A();
x=0;
}
第二种情况 基类中有默认构造函数(包括被合成的)
和第一种一样,你定义了很多构造函数,编译器会追加代码到每个构造函数中
第三种情况 带有虚函数的类 (包括纯虚函数)
编译器的必要的操作
1. 一个虚函数表将产生,内放虚函数的地址;
2. 一个虚函数指针将产生,指向虚函数表地址;
所以编译器会为每个对象设定vptr的初始值,放置适当的虚函数表地址,对于类所定义的构造函数追加代码完成这些事。
第四种情况 虚继承
class X{publci: int i ; };
class A :public virtual X { public : int j ;};
class B: public virtual X { public: double d;};
class C: public A,public B{ public: int k;};
void foo(const A *pa) { pa->i=1024;} //无法在编译期决定出pa->X::i 的位置
main() {foo(new A); foo(new C);} // 可能转变为 void foo (const A*pa) { pa->_vbcx->i=1024} vbcx是对象指向类X的指针,当然要在构造函数中确定。
拷贝构造函数
拷贝构造函数: 以一个对象的内容去初始化另个对象。 (关键在于初始化)
有三种情况下会调用拷贝构造函数
class x {……};
X x; X xx=x //不是赋值操作而是拷贝构造函数
Void foo (X x); // 对象参数
FooBar() { X x; return x;} // 返回对象
如果类没有提供显示的拷贝构造函数,编译器采用的是位逐次拷贝。也就是把对象数据成员值拷贝到另个对象上,如果遇到对象成员就会递归进去。
class String
{ public: //类没有提供显示的拷贝构造函数
private:
char *str ;
int len;
};
class word
{
public: //类没有提供显示的拷贝构造函数
private:
int occurs;
String str: //类含了对象成员
}
位逐次拷贝时先位逐次拷贝int occurs 遇到word后递归进 class String 位逐次拷贝。
当以下情况下编译器不会采用位逐次拷贝,而是生成默认的拷贝构造函数
1. 当类内含一个对象成员,而后者的类中声明了一个拷贝构造函数时 (包括程序员写的,编译器合成的)
2. 当类继承一个基类时,而后者存在一个拷贝构造函数时
3. 当类声明了虚函数时
4. 当类派生自一个继承链中有虚继承时
1.当类内含一个对象成员,而后者的类中声明了一个拷贝构造函数时
class String
{ public:
String (const char *);
String (const String &) //类提供显示的拷贝构造函数
~String();
private:
char *str ;
int len;
};
class word
{
public: //类没有提供显示的拷贝构造函数
private:
int occurs;
String str:
}
//就会合成一个 : word::word(const word &wd) { str.String::String( wd.str); occurs=wd.occurs;}
2 当类继承一个基类时,而后者存在一个拷贝构造函数时
class word :public String
{
public: //类没有提供显示的拷贝构造函数
private:
int occurs;
String str:
} //就会合成一个
3 当类声明了虚函数时
class word
{
public: //类没有提供显示的拷贝构造函数
virtural cout(); //类声明了虚函数
private:
int occurs;
String str:
} // 同理与构造函数一样的理由,拷贝构造函数要处理虚函数指针和虚函数表。
4 当类派生自一个继承链中有虚继承时
class Zoo{…}; class Racconn :public virtual Zoo {….} ; class ReadPanda : public Racconn {…};
这个时候不是一个类对象要另个类对象做初始化而是基类对象要派生类对象来初始化
Raccoonn rocky; Racconn little=rocky; // 简单的位逐次拷贝就行了
RedPanda litteRed; Racconnn littecritter = rocky ;
// 简单的位逐次拷贝就不行了,编译器必须明确地初始化littercritter 虚继承的指针。
作者名:曾凡坤, 又名曾牧暗鲨,网名:大白鲨 2003-7-27