C++ Traps之默认构造函数

首先申明的一点是:默认构造函数既可以是编译器自动合成的,也可以是用户自己定义的。当我们定义一个类对象时,如果没有显式地提供构造函数的参数,则会调用默认构造函数,然而,这一切的前提是我们有默认构造函数,因为编译器不会总给我们合成默认构造函数,只在“必需”的时候合成一个默认构造函数,一旦这个默认构造函数被合成了,其地位也是等同于用户自己定义的默认构造函数的,以后我会一一讲解这段话里涉及的知识点。


1、编译器没有合成默认构造函数,代码示例如下:


#include <iostream>
using namespace std;

class A {
public:
	int i;
	//A(int ii = 1) : i(ii) {}
};

void setStack(){
	int a = 2;
	cout<<"addr of int a: "<<&a<<endl;
}

void printA(){
	A a;
	cout<<"addr of  A  a: "<<&a<<endl;
	cout<<"content of a.i: "<<a.i<<endl;
}


int main()
{
	setStack();
	printA();
	return 0;
}

这段代码定义了一个类 A ,且没有提供用户自定义的默认构造函数,这段代码的目的是看看编译器有没有为我们合成一个默认构造函数(有的小伙伴认为编译器总是会合成一个默认构造函数来将类对象的成员初始化为零),代码中有两个函数,第一个函数中有一个 int 局部变量,我们给它初始化为 2 ,第二个函数里有一个 A 的对象,它的大小与 int 相同,如果这两个函数先后被调用的话,int 型的 a 与 A类型的 a 占用同一块内存(在栈上的同一块内存),所以我们可以在 main 中先调用 setStack 来初始化这块内存,再调用printA,这样就可以看到 A 的构造函数是否被合成出来了,代码的执行结果如下:



可以看到,int 型的 a 与 A型的 a 确实在同一块内存上,而且 A 的默认构造函数没有被合成,因此 A类的 a 中的数据成员 i 保留了之前内存中的值,故为 2, 我们可以给代码做一点点修改,就可以看到不同,现在我们给 A 定义一个默认构造函数(其余代码没有改动),如下:


class A {
public:
	int i;
	A(int ii = 1) : i(ii) {}
};

这时的输出结果变成了:



说明默认构造函数被调用了。现在我们可能就会问:到底什么时候编译器会为我们合成一个默认构造函数呢?在四种情况下编译器会给我们合成默认构造函数,分别如下:

a) 类中有其他类的对象(成员对象),而且该对象有默认构造函数;

b) 类继承与某个基类,而且该基类有默认构造函数;

c) 类中有虚函数;

d) 类虚继承于某个基类;


现在举个有虚函数的例子,代码如下:


#include <iostream>
using namespace std;

class A {
public:
	int i;
	//A(int ii = 1) : i(ii) {}
	virtual void fun() {}
};

void setStack(){
	int a = 3,b=4;
	cout<<"addr of int a: "<<&a<<endl;
	cout<<"addr of int b: "<<&b<<endl;
}

void printA(){
	A a;
	cout<<"addr of  A  a.vptr: "<<&a<<"-----content of a.vptr: "<<*((unsigned int*)&a)<<endl;
	cout<<"addr of  A  a.i   : "<<&a.i<<"-----content of a.i :   "<<a.i<<endl;
}


int main()
{
	setStack();
	printA();
	return 0;
}

这段代码给 A 定义了一个虚函数,众所周知,现在A的类对象中将包含一个指向虚函数表的指针,所以A的a将会占用8个字节的内存,同样地,我们在setStack中初始化8个字节的内存,代码的执行结果如下:




可以看到,默认构造函数是被合成了,但是该函数只初始化了指针的内容,没有初始化 i 的内容,因此依赖于自动合成的默认构造函数来初始化 i 仍然是不行的,换言之,对 i  的初始化并不是编译器必须做的,编译器认为应该由用户来负责对 i 的初始化,因此编译器只是初始化了虚函数表指针,所以我们不应该依赖编译器做很多事情,否则可能陷入不知所以的bug吐舌头


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是经典的C语言书籍推荐: 1. 《C程序设计语言》(The C Programming Language)- Brian W. Kernighan和Dennis M. Ritchie 2. 《C专家编程》(Expert C Programming)- Peter Van Der Linden 3. 《C和指针》(C Pointers and Memory Management)- Kamran Husain 4. 《C陷阱和缺陷》(C Traps and Pitfalls)- Andrew Koenig 5. 《C编程的艺术》(The Art of C Programming)- Eric S. Roberts 6. 《C语言深度剖析》(Deep C)- Olaf Chitil 7. 《C语言程序设计》(Programming in C)- Stephen G. Kochan 8. 《C语言核心技术》(C Core Language)- Doug Abbott 9. 《C程序员必读经典》(The C Programmer's Handbook)- Greg Perry 10. 《C语言参考手册》(C Pocket Reference)- Peter Prinz和Tony Crawford 11. 《C++ Primer》- Stanley B. Lippman、Josée Lajoie和Barbara E. Moo 12. 《C++ Primer Plus》- Stephen Prata 13. 《C++语言的设计与演化》(The Design and Evolution of C++)- Bjarne Stroustrup 14. 《C++程序设计》(C++ Programming)- DS Malik 15. 《C++标准库》(The C++ Standard Library)- Nicolai M. Josuttis 16. 《C++对象模型》(Inside the C++ Object Model)- Stanley B. Lippman 17. 《C++编程思想》(Thinking in C++)- Bruce Eckel 18. 《C++程序设计语言》(The C++ Programming Language)- Bjarne Stroustrup 19. 《C++沉思录》(C++ Puzzles and Paradoxes)- Steve Summit 20. 《C++编程规范》(C++ Coding Standards)- Herb Sutter和Andrei Alexandrescu 这些书籍对于学习C语言编程和C++编程非常有帮助,都是经典的教材和参考书籍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值