深度搜索C++对象模型 - 构造函数



深度搜索C++对象模型: 构造函数

Default Constructor 构造函数:
C++ARM告诉我们: Default Constructor在需要的时候被编译器产生出来.
关键字: 在需要的时候 --> 被谁需要 需要来干什么?
例子:
class Foo
{
 public:
  int m_val;
  Foo *next;
};

void FooBar()
{
 Foo bar;
 if( bar.m_val || bar.next )
 {
  //do something
 }
}

这个程序本身的语义是:要求Foo有一个默认的构造函数, 可以将内部的两个data member初始化为0;
但是实际上结果是不符合语义的; 他可曾符合ARM所说? 答案是no;
其间最重要的一点是:  被程序需要 ?  被编译器需要 ?
很自然 把两个data member初始化为0, 这是程序的需要, 这是需要程序员保证的;
而对于编译器来说 他根本就没有被需要; ( 所以根本不会有默认构造函数, 后面会讲 )

//*********************************************************************************************//

对于编译器来说 "被编译器需要的 有用的默认构造函数" 有4种 分别介绍:

//-----------------------------------------------------------------------------------------------------------------------//

---> "含有 带有Default Constructor 的 Member Class Object 的 class":
如果一个Class内没有任何Constructor, 但是他内含一个Member Class Object, 而且这个Mmeber Class Object有Default Constructor,
那么这个Class的隐式默认构造函数就是有用的;
例子:
class Foo
{
 public:
  Foo();
};

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

这种情况下, Bar里内含了一个成员类对象, 而且这个成员类对象含有默认构造函数, 那么编译器就会为class Bar合成一个默认构造函数;
当我们 Bar bar;的时候 Bar::foo 必须在此处被初始化, 被合成的Bar的默认构造函数内含必要的代码,能够调用class Foo的默认构造函数来初始化foo;
但是他并不产生任何代码来初始化str;  所以"将Bar::foo初始化是编译器的责任, 但是将Bar::str初始化是程序员的责任";
被合成的Bar的默认构造函数也许像这样:
inline
Bar::Bar()
{
 foo.Foo::Foo();
}


重申: "被合成的默认构造函数是编译器的需要 不是程序的需要";
至此 编译器的需要已经满足了, 所以我们要来满足程序的需要, 所以我们需要一个"显式默认构造函数"
Bar::Bar()
{
 str = nullptr;
}

此时, 程序的需要满足了 但是编译器还需要初始化member object foo ;
但是程序无法再次定义一个默认构造函数 因为我们已经显式的定义了一个,
此时 编译器行动如下: 如果class A内含一个或一个以上的member class object,
那么class A的"每一个构造函数"都会调用"每一个member calss object的默认构造函数";
编译器会扩张已存在的构造函数, 在其中安插一些代码, 使得我们user使用之前先执行默认构造,
如上 扩张后的默认构造函数可能如下:
Bar::Bar()
{
 foo.Foo::Foo();
 str = nullptr;
}

如果有多个member class object 则会按照声明的顺序来调用默认构造函数,
例如:
class A{ public: A(); };
class B{ public: B( int ); };
class C{ public: C(); };
class D{ public: A a; B b; C c; int num; };

如上: class D没有定义默认构造函数 则会有一个"有用的默认构造函数"被合成出来;
D::D() : b( 1024 )
{
 num = 2048;
}

那么他也许会被扩张为:
D::D : b( 1024 )
{
 a.A::A();
 b.B::B( 1024 );
 c.C::C();
 num = 2048;
}

//-----------------------------------------------------------------------------------------------------------------------//

---> "带有Default Constructor 的 Base Class":
如果一个没有任何constructor的class派生自一个"带有Default Constructor"的 base class,
那么这个 derived class 的默认构造函数为有用的;
例子:
class A{ public: int num_a; };
class B{ public: A a; int num_b; };
class C : public B

对于这个例子, class B是一个上文的 "含有 带有Default Constructor 的 Member Class Object 的 class";
那么 class C 派生自 class B; 那么 class C 的默认构造函数为有用的;
他将调用上一层( class B ) 的 "默认构造函数" 来 "构造其内含的 Member Class Object";
如果设计中提供多个构造函数,但是其中都"没有默认构造函数",
编译器会遍历所有构造函数, 内插上一层( class B )的构造函数 ( class B的构造函数又内插了Member Class Object的构造函数 );

//-----------------------------------------------------------------------------------------------------------------------//

---> "带有一个 Virtual Function 的 class":
对于一个 带有 virtual Function 的 class , 他的默认构造函数为有用的;

而一个 class 带有virtual Function 分为两种情况:
其一: 类申明( 或继承 ) 一个 virtual function;
其二: class 派生自一个继承串链, 其中有一个或多个 virtual base class ;

例子:
class Widget
{
 public:
  virtual void Func( const Wieget &idget ) = 0;
};

class Bell : public Widget
class Whistle : public Widget

Bell b;
Whistle w;
Func( b );
Func( w );

"两个扩张行动" 会在编译期间发生:
-> 一个 virtual function table ( vtbl )会被编译器产生出来, 其中存放着 class 的 virtual functions 地址;
-> 在每一个 class object 中, 一个额外的 pointer member ( vptr )会被编译器合成出来 内含有 virtual function table 的地址;
此外 weidget.Func() 的虚拟调用操作将被改写, 以使用 widget 的 vptr 和 vtbl 中的 Func() 条目;

为了让这个 "virtual function 机制" 发挥作用, 编译器必须"为每一个Widget Object 和 其派生类的Object 设置 vptr 的初值 放置适当的 vtbl地址";

对于 class 的每一个 Constructor ,编译器会安插一些代码来完成这个操作;

//-----------------------------------------------------------------------------------------------------------------------//

---> "带有一个 Virtual Base Class 的 Class":
class X
class A : public virtual X
class B : public virtual X
class C : public A, public B

带有 virtual Base Class 的 Class C, 他的构造函数是有用的;

//-----------------------------------------------------------------------------------------------------------------------//

总结: 以上4种情况, "编译器必须为未声明构造函数的类合成一个默认构造函数",
标准称这些合成出来的构造函数为"隐式有用的构造函数", 合成出来的构造函数能够满足编译器( 而非程序 )的需要,

至于"不属于以上4种情况的而且又没有声明构造函数" 标准称他们为"隐式无用的构造函数", 事实上他们"不会被合成出来";

在合成的"隐式有用的构造函数"中, 只有 base class subobjects ( 基类的子对象 ) 和 class member object ( 类成员对象 ) 会被初始化;
而其他的 data member, 等等都不会被初始化 ( 虽然他们也许对程序是有用的 );


---->"误解":
---> 任何class如果没有定义默认构造函数 就会被合成出来一个,
---> 编译器合成出来的默认构造函数会设定class内的每一个data member,
很明显 都是错误的,

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值