zoukankan      html  css  js  c++  java
  • 《新标准C++程序设计》3.1.1-3.1.3(C++学习笔记5)

    构造函数

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

    (1)概念

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

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

    注意:

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

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

    程序示例分析:

    ①默认构造函数

    class Complex {
    private:
        double real, imag;           //实部和虚部
    public:
        void Set(double r, double i);//设置实部和虚部
    };

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

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

    ②自己编写构造函数

    class Complex {
    private:
        double real, imag;           
    public:
        Complex(double r, double i = 0);  //第二个参数的默认值为0
    };
    Complex::Complex(double r, double i)
    {
        real = r;
        imag = i;
    }

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

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

    ③有多个构造函数

    class Complex {
    private:
        double real, imag;
    public:
        Complex(double r);
        Complex(double r, double i);
        Complex(Complex c1, Complex c2);
    };
    Complex::Complex(double r)              //构造函数1
    {
        real = r;
        imag = 0;
    }
    Complex::Complex(double r, double i)    //构造函数2
    {
        real = r;
        imag = i;
    }
    Complex::Complex(Complex c1, Complex c2)//构造函数3
    {
        real = c1.real + c2.real;
        imag = c1.imag + c2.imag;
    }
    int main()
    {
        Complex c1(3), c2(1, 2), c3(c1, c2), c4 = 7;
        return 0;
    }

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

    输出结果:

    Constructor 1 Called
    Constructor 1 Called
    step1
    Constructor 2 Called
    Constructor 2 Called
    step2
    Constructor 2 Called
    Constructor 1 Called
    step3
    Constructor 1 Called
    Constructor 1 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)在构造函数有多个参数时,数组的初始化列表中要显示包含对构造函数的调用

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

    3、复制构造函数的概念

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

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

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

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

    程序示例分析:

    (1)默认复制构造函数

    #include<iostream>
    using namespace std;
    class Complex
    {
    public:
        double real, imag;
        Complex(double r, double i) {
            real = r;
            imag = i;
        }
    };
    int main() {
        Complex c1(1, 2);
        Complex c2(c1);                     //用复制构造函数初始化c2
        cout << c2.real << "," << c2.imag;  //输出1,2
        return 0;
    }

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

    #include<iostream>
    using namespace std;
    class Complex
    {
    public:
        double real, imag;
        Complex(double r, double i) {
            real = r;
            imag = i;
        }
        Complex(const Complex& c)            //复制默认构造函数
        {
            real = c.real;
            imag = c.imag;
            cout << "Copy Constructor called" << endl;
        }
    };
    int main() {
        Complex c1(1, 2);
        Complex c2(c1);                     //调用复制构造函数
        cout << c2.real << "," << c2.imag; 
        return 0;
    }

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

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

    Complex c2(c1);
    Complex c2=c1;

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

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

    Complex c1,c2;
    c1=c2;

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

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

    #include<iostream>
    using namespace std;
    class A
    {
    public:
        A() {};
        A(A& a) //复制构造函数
        {
            cout << "Copy constructor called" << endl;
        }
    };
    void Func(A a){}
    int main() {
        A a;
        Func(a);
        return 0;
    }

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

    void Function(const Complex &c)
    {
        ···
    }

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

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

    #include<iostream>
    using namespace std;
    class A
    {
    public:
        int v;
        A(int n) { v = n; };
        A(const A& a) {
            v = a.v;
            cout << "Copy constructor called" << endl;
        }
        A() {};
        A(A& a) //复制构造函数
        {
            cout << "Copy constructor called" << endl;
        }
    };
    A Func() {
        A a(4);
        return a;
    }
    int main() {
        cout << Func().v << endl;
        return 0;
    }

    5、常量引用参数的使用

    void fun(CMyclass obj_)
    {
        cout << "fun" << endl;
    }

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

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

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

    void fun(const CMyclass &obj)
    {
        //函数中任何试图改变obj值的语句都将是变成非法
    }
  • 相关阅读:
    Python 列表浅拷贝与深拷贝
    Linux 基本命令-----常用操作分类
    硬盘的分区方式
    github中fork的使用
    大O记号
    python的__file__和__name__变量
    python生成器
    python装饰器
    re模块元字符
    python_数据类型_list
  • 原文地址:https://www.cnblogs.com/cyn522/p/12250689.html
Copyright © 2011-2022 走看看