Fork me on GitHub

C++——构造函数 constructor

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 }
View Code

调试结果

 这里调用了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 }
View Code
 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 }
View Code

这种情况直接编译不过。错误 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 }
View Code

在进行赋值 或 类型转换的时候,如果要借助构造函数是不允许隐式转换的。所以第20行必须强制转换(显式转换)。

 

posted @ 2018-08-05 21:30  克拉默与矩阵  阅读(3495)  评论(0编辑  收藏  举报