构造函数

1、构造函数的概念和作用

(1)概念

构造函数就是一类特殊的成员函数,其名字和类一样,不写返回值类型(void也不可以写),可以有参数,可以重载。

如果定义类时没写构造函数,则编译器生成一个默认的无参的构造函数 ,默认构造函数无参数,不做任何操作。如果定义了构造函数,则编译器不生成默认的无参数的构造函数

注意:

一个类可以有多个构造函数。

对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数 。

程序示例分析:

①默认构造函数

  1. class Complex {
  2. private:
  3. double real, imag; //实部和虚部
  4. public:
  5. void Set(double r, double i);//设置实部和虚部
  6. };

编译器自动生成默认构造函数

  1. Complex c; //c用无参构造函数初始化
  2. Complex* p = new Complex; //对象*p用无参构造函数初始化

②自己编写构造函数

  1. class Complex {
  2. private:
  3. double real, imag;
  4. public:
  5. Complex(double r, double i = ); //第二个参数的默认值为0
  6. };
  7. Complex::Complex(double r, double i)
  8. {
  9. real = r;
  10. imag = i;
  11. }

以下语句有些能通过编译,有些则不行:

  1. Complex c1; //错,Complex类没有无参构造函数(默认构造函数)
  2. Complex* pc = new Complex; //错,Complex类没有默认构造函数
  3. Complex c2(); //正确,相当于Complex c2(2,0)
  4. Complex c3(, ), c4(, ); //正确
  5. Complex* pc2 = new Complex(, );//正确

③有多个构造函数

  1. class Complex {
  2. private:
  3. double real, imag;
  4. public:
  5. Complex(double r);
  6. Complex(double r, double i);
  7. Complex(Complex c1, Complex c2);
  8. };
  9. Complex::Complex(double r) //构造函数1
  10. {
  11. real = r;
  12. imag = ;
  13. }
  14. Complex::Complex(double r, double i) //构造函数2
  15. {
  16. real = r;
  17. imag = i;
  18. }
  19. Complex::Complex(Complex c1, Complex c2)//构造函数3
  20. {
  21. real = c1.real + c2.real;
  22. imag = c1.imag + c2.imag;
  23. }
  24. int main()
  25. {
  26. Complex c1(), c2(, ), c3(c1, c2), c4 = ;
  27. return 0;
  28. }

c1用构造函数1,初始化结果是:c1.real=3,c1.imag=0(或c1={3,0})

c2用构造函数2,初始化结果是:c2.real=1,c2.imag=2(或c1={1,2})

c3用构造函数3,初始化结果是:c3.real=4,c3.imag=2(或c1={4,2})

c4用构造函数4,初始化结果是:c4.real=7,c4.imag=0(或c1={7,0})

(2)作用

对对象进行初始化,如给成员变量赋初值

(3)为什么需要构造函数

① 构造函数执行必要的初始化工作,有了构造函数,就不 必专门再写初始化函数,也不用担心忘记调用初始化函数。

② 有时对象没被初始化就使用,会导致程序出错。

2、构造函数在数组中的使用

程序示例分析:

(1)

  1. 1.#include<iostream>
  2. 2.using namespace std;
  3. 3.class CSample {
  4. 4.public:
  5. 5. CSample()//构造函数1{
  6. 6. cout << "Constructor 1 Called" << endl;
  7. 7. }
  8. 8. CSample()//构造函数2{
  9. 9. cout << "Constructor 2 Called" << endl;
  10. 10. }
  11. 11.};
  12. 12.int main(){
  13. 13. CSample array1[];
  14. 14. cout << "step1" << endl;
  15. 15. CSample array2[] = { , };
  16. 16. cout << "step2" << endl;
  17. 17. CSample array3[] = { };
  18. 18. cout << "step3" << endl;
  19. 19. CSample* array4 = new CSample[];
  20. 20. delete[]array4;
  21. 21. return ;
  22. 22.}

输出结果:

  1. Constructor Called
  2. Constructor Called
  3. step1
  4. Constructor Called
  5. Constructor Called
  6. step2
  7. Constructor Called
  8. Constructor Called
  9. step3
  10. Constructor Called
  11. Constructor Called

分析:

第13行的array1数组中的两个元素没有明确指明如何初始化,要用无参构造函数初始化,因此输出两行Constructor 1 Called

第15行的array2数组进行了初始化,初始化列表{4,5}可以看作用来初始化两个数组元素的参数,所以array2[0]以4为参数,调用构造函数2进行初始化;array2[1]以5为参数,调用构造函数2进行初始化。这导致输出两行Constructor 2 Called

第17行的array3只指出了array3[0]的初始化方式,没有指出array3[1]的初始化方式,因此它们分别用构造函数2和构造函数1进行初始化

第19行动态分配了一个CSample数组,其中有两个元素,没有指出和参数有关信息,因此两个元素都用无参构造函数初始化

(2)在构造函数有多个参数时,数组的初始化列表中要显示包含对构造函数的调用

  1. class CTest {
  2. public:
  3. CTest(int n){} //构造函数1
  4. CTest(int n, int m) {} //构造函数2
  5. CTest(){} //构造函数3
  6. };
  7. int main() {
  8. CTest array1[] = { ,CTest(,) }; //三个元素分别用构造函数1,2,3初始化
  9. CTest array2[] = { CTest(,),CTest(,), }; //三个元素分别用构造函数2,2,1初始化
  10. CTest* pArray[] = { new CTest(),new CTest(,) };//两个元素指向的对象分别用构造函数1,2初始化
  11. return ;
  12. }

3、复制构造函数的概念

只有一个参数,即对同类对象的引用。

形如 X::X( X& )或X::X(const X &), 二者选一 ,后者能以常量对象作为参数,适用范围更广一些

如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。

注意:不允许有形如 X::X( X )的构造函数

程序示例分析:

(1)默认复制构造函数

  1. #include<iostream>
  2. using namespace std;
  3. class Complex
  4. {
  5. public:
  6. double real, imag;
  7. Complex(double r, double i) {
  8. real = r;
  9. imag = i;
  10. }
  11. };
  12. int main() {
  13. Complex c1(, );
  14. Complex c2(c1); //用复制构造函数初始化c2
  15. cout << c2.real << "," << c2.imag; //输出1,2
  16. return ;
  17. }

(2)非默认复制构造函数

  1. #include<iostream>
  2. using namespace std;
  3. class Complex
  4. {
  5. public:
  6. double real, imag;
  7. Complex(double r, double i) {
  8. real = r;
  9. imag = i;
  10. }
  11. Complex(const Complex& c) //复制默认构造函数
  12. {
  13. real = c.real;
  14. imag = c.imag;
  15. cout << "Copy Constructor called" << endl;
  16. }
  17. };
  18. int main() {
  19. Complex c1(, );
  20. Complex c2(c1); //调用复制构造函数
  21. cout << c2.real << "," << c2.imag;
  22. return ;
  23. }

4、复制构造函数被调用(起作用)的三种情况

①当用一个对象去初始化同类的另一个对象时。

  1. Complex c2(c1);
  2. Complex c2=c1;

这两条语句是等价的。第二条语句是初始化语句,不是赋值语句。

赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发复制构造函数的调用

  1. Complex c1,c2;
  2. c1=c2;

②如果某函数有一个参数是类A 的对象, 那么该函数被调用时,类A的复制构造函数将被调用。

换句话说,作为形参的对象,是用复制构造函数初始化的,而且调用复制构造函数时的参数,就是调用函数时所给的实参。

  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6. A() {};
  7. A(A& a) //复制构造函数
  8. {
  9. cout << "Copy constructor called" << endl;
  10. }
  11. };
  12. void Func(A a){}
  13. int main() {
  14. A a;
  15. Func(a);
  16. return ;
  17. }

以对象作为函数的形参,在函数被调用时,生成的形参要用复制构造函数初始化,这会带来时间上的开销。如果用对象的引用而不是对象作为形参,就没有这个问题了。但是以引用作为形参有一定的风险,因为这种情况下如果形参的值发生改变,实参的值也会跟着改变。如果要确保实参的值不会改变,又希望避免复制构造函数带来的开销,解决办法就是将形参声明为对象的const引用。

  1. void Function(const Complex &c)
  2. {
  3. ···
  4. }

③如果函数的返回值是类A的对象时,则函数返回时, A的复制构造函数被调用

换言之,作为函数返回值的对象是用复制构造函数初始化的,而调用复制构造函数是的实参,就是return语句所返回的对象。

  1. #include<iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6. int v;
  7. A(int n) { v = n; };
  8. A(const A& a) {
  9. v = a.v;
  10. cout << "Copy constructor called" << endl;
  11. }
  12. A() {};
  13. A(A& a) //复制构造函数
  14. {
  15. cout << "Copy constructor called" << endl;
  16. }
  17. };
  18. A Func() {
  19. A a();
  20. return a;
  21. }
  22. int main() {
  23. cout << Func().v << endl;
  24. return ;
  25. }

5、常量引用参数的使用

  1. void fun(CMyclass obj_)
  2. {
  3. cout << "fun" << endl;
  4. }

这样的函数,调用时生成形参会引发复制构造函数调用,开销比较大。

所以可以考虑使用 CMyclass & 引用类型作为参数。

如果希望确保实参的值在函数中不应被改变,那么可以加上const 关键字

  1. void fun(const CMyclass &obj)
  2. {
  3. //函数中任何试图改变obj值的语句都将是变成非法
  4. }

《新标准C++程序设计》3.1.1-3.1.3(C++学习笔记5)的更多相关文章

  1. 《新标准C++程序设计》3.1.4-3.2(C++学习笔记6)

    1.类型转换构造函数 (1)定义 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数. 当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量). ( ...

  2. 正确处理类的复合关系------新标准c++程序设计

    假设要编写一个小区养狗管理程序,该程序需要一个“主人”类,还需要一个“狗”类.狗是有主人的,主人也有狗.假定狗只有一个主人,但一个主人可以有最多10条狗.该如何处理“主人”类和“狗”类的关系呢?下面是 ...

  3. 在成员函数中调用虚函数(关于多态的注意事项)------新标准c++程序设计

    类的成员函数之间可以互相调用.在成员函数(静态成员函数.构造函数和析构函数除外)中调用其他虚成员函数的语句是多态的.例如: #include<iostream> using namespa ...

  4. 多态实现的原理------新标准c++程序设计

    “多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定.例子: #include<iostream> using namespac ...

  5. 多态的作用-游戏编程展示------新标准c++程序设计

    游戏软件的开发最能体现面向对象设计方法的优势.游戏中的人物.道具.建筑物.场景等都是很直观的对象,游戏运行的过程就是这些对象相互作用的过程.每个对象都有自己的属性和方法,不同对象也可能有共同的属性和方 ...

  6. 类与类之间的两种关系------新标准c++程序设计

    在c++中,类和类之间有两种基本关系:复合关系和继承关系. 复合关系也称为“has a”关系或“有”的关系,表现为封闭类,即一个类以另一个类的对象作为成员变量. 继承关系也称为“is a”关系或“是” ...

  7. 复制构造函数被调用的三种情况------新标准c++程序设计

    1.当用一个对象去初始化同类的另一个对象时,会引发复制构造函数被调用.例如,下面的两条语句都会引发复制构造函数的调用,用以初始化c2. C c2 (c1); C c2=c1; 这两条语句是等价的.注意 ...

  8. 析构函数的调用------新标准c++程序设计

    示例1: #include<iostream> using namespace std; class CDemo{ public: ~CDemo(){cout<<"d ...

  9. 类型转换构造函数 及使用explicit避免类型自动转换------新标准c++程序设计

    类型转换构造函数:  除复制构造函数外,只有一个参数的构造函数一般可以称作类型转换构造函数,因为这样的构造函数能起到类型自动转换的作用.例如下面的程序: #include<iostream> ...

  10. this指针------新标准c++程序设计

    背景:   c++是在c语言的基础上发展而来的,第一个c++的编译器实际上是将c++程序翻译成c语言程序,然后再用c语言编译器进行编译.c语言没有类的概念,只有结构,函数都是全局函数,没有成员函数.翻 ...

随机推荐

  1. 使用Go语言一段时间的感受

    作者 openkk 2012-03-04 18:26:58 文/Windstorm 有一段时间没更新了.最近在忙一个 Server+Client 的项目,Client 是 Android 手机,大概也 ...

  2. Lamda简单使用

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  3. Day3:关于地形生成

    ---恢复内容开始--- 今天桃子好像还是没什么动静,不过媳妇倒是有一点见红~ 希望这是马上要出来的前兆了~ 桃子都已经晃点我俩好多回了~ 已经都快习惯来她这个狼来了的征兆了~ ----------- ...

  4. 吴裕雄--天生自然JAVA面向对象高级编程学习笔记:包装类

    public class WrapperDemo01{ public static void main(String args[]){ int x = 30 ; // 基本数据类型 Integer i ...

  5. 2017北京网络赛 J Pangu and Stones 区间DP(石子归并)

    #1636 : Pangu and Stones 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In Chinese mythology, Pangu is the fi ...

  6. C语言整理复习——指针

    指针是C的精华,不会指针就等于没学C.但指针又是C里最难理解的部分,所以特意写下这篇博客整理思路. 一.指针类型的声明 C的数据类型由整型.浮点型.字符型.布尔型.指针这几部分构成.前四种类型比较好理 ...

  7. Aop配置时候的一些问题

    编写切面,并在ApplicationContest.xml 中相关AOP及切面的配置完全正确的情况下,运行报错如下: Exception in thread "main" org. ...

  8. Android的事件处理机制之基于回调的事件处理

    回调机制 如果说事件监听机制是一种委托式的事件处理,那么回调机制则与之相反,对于基于回调的事件处理模型来说,事件源与事件监听器是统一的,换种方法说事件监听器完全消失了,当用户在GUI组件上激发某个事件 ...

  9. R 正态性检验:正态概率图

    检验模型是否满足正态性假设的方法: 1.正态概率图 这是我编写的画正态概率图的函数: #绘制正态概率图 plot_ZP = function(ti) #输入外部学生化残差 { n = length(t ...

  10. 虚拟机下安装Maven

    1.首先需要下载maven安装包(我下载的是apache-maven-3.5.3版本) 官网下载:http://maven.apache.org/download.cgi 2.将压缩包放到虚拟机下(我 ...