What is constructor
- C++中,如果你想要创建一个object,有一个函数会自动被调用(不需要programmer显式调用 ),这个函数就是constructor;
- constructor的写法很独特,其function name必须和class name相同;
- constructor通过arguments上的差异进而形成ctor的overloading,当然和normal function一样,ctor也可以有default arguments;
- ctor没有返回值类型;
- ctor还有个特色功能(ctor独享),initialization list(初始化列表)。与在{ ... }内部赋值private data,initialization list速度会更快。简单讲,一个variable数值的设定有2个阶段,分别是initialization和assignment,initialization先于assignment阶段进行。initialization list就是发生在initialization阶段,{ ... }内部赋值private data属于assignment阶段。如果不使用initialization list表明你主动放弃initialization阶段工作,虽然最后你还是把数值放入到variable,但是时间晚了些,效率差了些;
默认实参,构造函数,普通函数都有这种性质。
ctor's overloading
参考:C++——overloading principle analysis
关于header file、static、inline、variable hides的一点感想
我们知道C不允许overloading,C++允许overloading。C++在编译器一侧时别到的函数名与我们看到的函数名是有很大差异的。normal function和ctor的overloading方式差不多。下图事normal function的重载。
注意:黄色部分①②两个ctor则不可以同时存在,①对于的ctor所有形参都有默认参数,这时候就相当于default ctor,和②是一样的效果。编译器在这种情况下无法做出选择。虽然这也是overloading,但是这种overloading让compiler困惑
常量成员函数
在函数后头加const,表示函数不会改变调用者的内容,或 形参在函数内部不会改变。
如果忘记加const,右下角的例子,c1是个常量,而real和imag却有可能改变c1,产生矛盾,编译器报错。
singleton——把构造函数放在private区
design patrern中 的一种模式
构造函数的作用
①构造对象:在用类定义一个对象时会自动调用构造函数
②初始化对象:类的数据成员往往放在构造函数里面初始化
③类型转换:看讲解
无论C 、C++都是强类型语言,不同类型变量之间不能随便赋值。很多时候我们认为理所当然的赋值都是借助临时变量来实现的。看代码
1 #include<iostream> 2 class Test { 3 public: 4 Test(int a=0) 5 { 6 std::cout << "Create Test Object:" << this<<std::endl; 7 this->a = a; 8 } 9 ~Test() 10 { 11 std::cout << "Free Test Object:" << this << std::endl; 12 } 13 private: 14 int a; 15 }; 16 17 int main(int argc, char **argv) 18 { 19 Test t1; 20 t1 = 100; 21 getchar(); 22 return 0; 23 }
调试结果
这里调用了2此构造函数,2次析构函数(析构顺序是栈操作顺序)。输出结果解析
第1行输出:执行第19行,调用构造函数
第2行输出:执行第20行,调用构造函数。这里创建了一个临时Test类型对象(C++为了凸显逼格把类类型的变量称为对象),100就是传入构造函数的参数。
第3行输出:第2行构造完临时对象后,赋值给了对象t1。赋值以后临时对象的生命期就结束了,这里调用析构函数,终结这个临时对象
第4行输出:第22行return 0返回后调用析构函数,终结对象t1
画图解释这一过程
这里面最关键的一个环节就是int类型到Test临时对象转换,int类型要找到一个途径来转换成Test类型。这个途径是什么呢? 构造函数
而我们代码里面恰恰有这么个构造函数,他的参数就是一个整形。如果我们干掉这个构造函数,换一个不带参数的构造函数(或者干脆不写构造函数,使用默认的),则不能将100赋值给t1
代码如下
1 #include<iostream> 2 class Test { 3 public: 4 Test() 5 { 6 std::cout << "Create Test Object:" << this << std::endl; 7 this->a = a; 8 } 9 ~Test() 10 { 11 std::cout << "Free Test Object:" << this << std::endl; 12 } 13 private: 14 int a; 15 }; 16 17 int main(int argc, char **argv) 18 { 19 Test t1; 20 t1 = 100; 21 getchar(); 22 return 0; 23 }
1 #include<iostream> 2 class Test { 3 public: 4 ~Test() 5 { 6 std::cout << "Free Test Object:" << this << std::endl; 7 } 8 private: 9 int a; 10 }; 11 12 int main(int argc, char **argv) 13 { 14 Test t1; 15 t1 = 100; 16 getchar(); 17 return 0; 18 }
这种情况直接编译不过。错误 C2679 二进制“ = ”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)。在类型转换的时候,编译器试图寻找能实现转换的构造函数,能找到就执行构造函数生成一个临时对象好用于赋值。找不到就报错。
额外补充:对于隐式类型转换,生成的临时对象具有const性质。参考 C++——引用
explicit关键字
有的时候我们会发现构造函数前面加了个关键字explicit,代码如下
1 #include<iostream> 2 class Test { 3 public: 4 explicit Test(int a = 0) 5 { 6 std::cout << "Create Test Object:" << this << std::endl; 7 this->a = a; 8 } 9 ~Test() 10 { 11 std::cout << "Free Test Object:" << this << std::endl; 12 } 13 private: 14 int a; 15 }; 16 17 int main(int argc, char **argv) 18 { 19 Test t1; 20 t1 = (Test)100; 21 getchar(); 22 return 0; 23 }
在进行赋值 或 类型转换的时候,如果要借助构造函数是不允许隐式转换的。所以第20行必须强制转换(显式转换)。