一:背景
在C#中要说类默认给我们定义的特殊成员函数,莫过于构造函数,但在C++中这样的特殊函数高达6种,有必要整合一下聊一聊。
二:特殊成员函数
1.默认构造函数
和C#一样,很多书中都说,如果用户没有定义构造函数,那么编译器会给我们定义一个,参考下面的例子:
classPerson{public: stringname; intage;};intmain(){ Personperson;}
接下来观察下汇编代码,看下有没有调用默认构造函数.
Personperson;E32EFleaecx,[person]E32F2callPerson::Person(03E15EBh)
对于C#学习者来说有点懵哈,定义了就相当于new了,哈哈,这是因为C++默认都是值类型哈,不过这里有必要澄清一下,并不一定所有情况都会调用默认构造函数,因为C++的汇编生成由各自编译器来决定,如果编译器觉得没必要调用构造函数那它就会把这一步省掉来加速性能,那什么时候不会调呢?参考如下代码。
classPerson{public: voidshow(){ printf("show!"); }};intmain(){ Personperson; person.show();}
接下来看下汇编代码。
person.show();00E73F4Fleaecx,[person]00E73F52callPerson::show(0EB6h)
可以清楚的看到,这种情况下调用构造函数其实没有必要,所以编译器就干脆省略了。
2.析构函数
在C#中析构函数是由CLR负责管理,在C++中没有托管这个概念,所以默认只能是结束作用域之前,自动调用析构函数释放,参考如下图:
3.赋值构造函数
刚才也说到了,在C++中甭管是class还是struct默认都是值类型,既然是值类型就存在stackcopy的情况,在C#中也是因为重写了Equals和GetHashCode来实现的值copy,接下来简单看下代码:
classPerson{public: stringname; intage;};intmain(){ Personp1={"jack",20}; Personp2(p1);}
再看下Personp2(p1)的汇编代码。
Personp2(p1);F80A2leaeax,[p1]F80A5pusheaxF80A6leaecx,[p2]F80A9callPerson::Person(0F15C3h)
从汇编中可以看到调用了Person::Person(0F15C3h)函数,请注意,这个不是构造函数,而是赋值构造函数,可以调试下去看看哦。。。截图如下:
值得说一下的是,C++默认提供的赋值构造函数是浅copy,如果要实现深copy的话,或者有一些自定义的逻辑,建议自己实现一下。
classPerson{public: stringname; intage;public: Person(stringname,intage):name(name),age(age){} Person(constPersonp){ name=p.name; age=p.age; }};intmain(){ Personp1={"aaaaaaaaaaaaaaaaaaaaaaaaaaa",20}; Personp2(p1);}
4.赋值运算符
在C#中值类型,匿名类型,Record都是重写过Equals及=运算符,所以可以在这些类型上用=,其实在C++中也可以在class之间进行赋值,因为编译器会帮我们重写运算符=,如何看出来呢?先看下代码:
classPerson{public: stringname; intage;public: Person(stringname,intage):name(name),age(age){} Person(constPersonp){ name=p.name; age=p.age; }};intmain(){ Personp1={"aaaaaaaaaaaaaaaaaaaaaaaaaaa",20}; Personp2={"bbbbbbbbbbbbbbbbbbbbbbbbbbb",22}; p2=p1;}
最后一句的p2=p1之所以能成功是因为=被重写了,参考汇编代码。
p2=p1;00FDCleaeax,[p1]00FDFpusheax00FDleaecx,[p2]00FDcallPerson::operator=(0FDDh)
如果需要自定义,可以自己重写。
classPerson{public: stringname; intage;public: Person(stringname,intage):name(name),age(age){} Person(constPersonp){ name=p.name; age=p.age; } Personoperator=(constPersonp){ name=p.name; age=p.age; turn*this; }};intmain(){ Personp1={"aaaaaaaaaaaaaaaaaaaaaaaaaaa",20}; Personp2={"bbbbbbbbbbbbbbbbbbbbbbbbbbb",22}; p2=p1;}
在C++11中还有特殊的移动构造函数和移动赋值构造函数,这个还需要理解左值和右值引用,篇幅有限,放到后面和大家聊了哈。