C++ Annotated Reference Manual中告诉我们:default constructors 在需要的时候被编译器产生出来;那么被谁需要?又做了什么事情呢?
#include
class Foo { // 不会合成出一个default constructor
public:
int val;
Foo *pNext;
};
void FooBar()
{
Foo bar;
if (bar.val || bar.pNext) {
printf("bar's members is not all zero.\n"); // 实际运行分支
} else {
printf("bar's member is all zero).\n"); // 按照程序的预期,应该运行该分支
}
printf("bar.val:%d bar.pNext:%p \n", bar.val, bar.pNext);
}
int main()
{
FooBar();
return 0;
}
这个例子中,正确的程序语意是Foo有个default constructor,可以将它的两个member初始化为0;然而真正的输出是:
bar’s members is not all zero.
bar.val:4196352 bar.pNext:0x400650
两个member并没有被初始化为0;回头再看“default constructors 在需要的时候被编译器产生出来”这句话,这里要注意差别,该例子中是程序的需要,这句话是编译器的需要;程序的需要是程序员的责任,所以上述例子不会合成出一个default constructor;
可以使用二进制查看工具查看二进制中的是否存在构造函数的FUNC
1、没有生成默认构造函数的结果如下(即上述代码)
root@xxx:/home/yms/Test# readelf -s main | grep FUNC
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
30: 00000000004004a0 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
31: 00000000004004e0 0 FUNC LOCAL DEFAULT 14 register_tm_clones
32: 0000000000400520 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
35: 0000000000400540 0 FUNC LOCAL DEFAULT 14 frame_dummy
47: 0000000000400630 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
52: 0000000000400634 0 FUNC GLOBAL DEFAULT 15 _fini
53: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
54: 0000000000400566 73 FUNC GLOBAL DEFAULT 14 _Z6fooBarv
55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _libc_start_main@@GLIBC
60: 00000000004005c0 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
62: 0000000000400470 42 FUNC GLOBAL DEFAULT 14 _start
64: 00000000004005af 16 FUNC GLOBAL DEFAULT 14 main
68: 0000000000400400 0 FUNC GLOBAL DEFAULT 11 _init
2、将上述Foo类修改成如下
class Demo {
public:
Demo(){};
};
class Foo {
public:
int val;
Foo *pNext;
Demo demo;
};
再看二进制中FUNC有哪些:
root@xxx:/home/yms/Test# readelf -s main | grep FUNC
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
30: 0000000000400510 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
31: 0000000000400550 0 FUNC LOCAL DEFAULT 14 register_tm_clones
32: 0000000000400590 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
35: 00000000004005b0 0 FUNC LOCAL DEFAULT 14 frame_dummy
47: 0000000000400700 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
51: 000000000040066a 31 FUNC WEAK DEFAULT 14 _ZN3FooC2Ev // // 此处为编译器产生的默认构造函数
53: 0000000000400704 0 FUNC GLOBAL DEFAULT 15 _fini
54: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@@GLIBC_2
55: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@@GLIBC_2.2.5
56: 000000000040065e 11 FUNC WEAK DEFAULT 14 _ZN4DemoC2Ev
57: 00000000004005d6 120 FUNC GLOBAL DEFAULT 14 _Z6fooBarv
58: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _libc_start_main@@GLIBC
59: 000000000040066a 31 FUNC WEAK DEFAULT 14 _ZN3FooC1Ev // 此处为编译器产生的默认构造函数
64: 000000000040065e 11 FUNC WEAK DEFAULT 14 _ZN4DemoC1Ev
65: 0000000000400690 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
67: 00000000004004e0 42 FUNC GLOBAL DEFAULT 14 _start
69: 000000000040064e 16 FUNC GLOBAL DEFAULT 14 main
73: 0000000000400460 0 FUNC GLOBAL DEFAULT 11 _init
为什么有两个构造函数的符号,可以参考此回答
符号名字含义:
_Z | N | 3Foo | C1 | E | v
prefix | nested |Foo
| Constructor | end nested | parameters:void
tips:另外还需注意,被编译器合成出来的constructor只执行编译器所需的行动,也就是说合成的default constructor不会将两个data members 初始化为0。
答案是当编译器需要它的时候;那么什么情况下编译器会需要它呢,分为下面4种情况
#include
#include
class Demo1 {
public:
Demo1() {
printf("Demo1 constructor\n");
};
};
class Demo2 {
public:
Demo2() {
printf("Demo2 constructor\n");
};
};
class Foo {
public:
Foo() {
printf("Foo constructor\n");
};
int val;
Foo *pNext;
Demo2 demo2;
Demo1 demo1;
};
void fooBar()
{
Foo bar;
}
int main() {
fooBar();
return 0;
}
输出:
root@xxx:/home/yms/Test# ./main
Demo2 constructor
Demo1 constructor
Foo constructor
#include
#include
class Base {
public:
Base(){
printf("Base constructor\n");
};
};
class Foo : public Base {
public:
int val;
Foo *pNext;
};
void fooBar()
{
Foo bar;
}
int main() {
fooBar();
return 0;
}
root@DAVINCI-D01-001:/home/yms/Test# readelf -s main1 | grep FUNC
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.2.5 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 (2)
30: 00000000004004d0 0 FUNC LOCAL DEFAULT 14 deregister_tm_clones
31: 0000000000400510 0 FUNC LOCAL DEFAULT 14 register_tm_clones
32: 0000000000400550 0 FUNC LOCAL DEFAULT 14 __do_global_dtors_aux
35: 0000000000400570 0 FUNC LOCAL DEFAULT 14 frame_dummy
47: 0000000000400690 2 FUNC GLOBAL DEFAULT 14 __libc_csu_fini
50: 0000000000000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.2.5
51: 00000000004005fa 27 FUNC WEAK DEFAULT 14 _ZN3FooC2Ev
53: 0000000000400694 0 FUNC GLOBAL DEFAULT 15 _fini
54: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@@GLIBC_2
55: 0000000000400596 58 FUNC GLOBAL DEFAULT 14 _Z6fooBarv
56: 00000000004005e0 25 FUNC WEAK DEFAULT 14 _ZN4BaseC1Ev
57: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _libc_start_main@@GLIBC
58: 00000000004005fa 27 FUNC WEAK DEFAULT 14 _ZN3FooC1Ev
62: 00000000004005e0 25 FUNC WEAK DEFAULT 14 _ZN4BaseC2Ev
64: 0000000000400620 101 FUNC GLOBAL DEFAULT 14 __libc_csu_init
66: 00000000004004a0 42 FUNC GLOBAL DEFAULT 14 _start
68: 00000000004005d0 16 FUNC GLOBAL DEFAULT 14 main
72: 0000000000400428 0 FUNC GLOBAL DEFAULT 11 _init
运行结果:
Base constructor
后面两种情况,大致都是为了满足多态,需在当前class中新增一个“指向虚表”的指针,这个指针需要在默认构造函数中初始化,virtual function机制或virtual base class机制,后序文章详细介绍。
上述4种情况,会造成编译器必须为未声明constructor的class合成一个default constructor。C++ Standard把那些合成物称为implicit nontrival default constructor。被合成的constructor只能满足编译器(非程序)的需要。
在合成的default constructor中,只有base class subobjects和member class objects会被初始化,所有其他的nonstatic data member都不会被初始化。
全是假的。