《C++面向对象程序设计》课程笔记 lessen2

  • Post author:
  • Post category:其他


1. 构造函数

构造函数是成员函数的一种。名字与类名相同,可以有参数,但

不能有返回值(void 也不行)。


构造函数的作用是对对象进行初始化,如给成员变量赋初值。

编译器生成的默认构造函数无参数,不做任何操作。


对象生成时构造函数自动被调用。

对象一旦生成,就再也不能在其上执行构造函数。一个类可以有多个构造函数。


构造函数的意义:

  • 自动初始化对象,不再需要额外初始化对象。(不必专门再编写初始化函数,不用担心忘记调用初始化函数。)
  • 避免无初始化导致的未知错误。(对象需先初始化再被使用。对象没被初始化就被使用,会导致程序出错。)


构造函数基本使用

class Complex {
    double real, imag;  // private
public:
    Complex(double r, double i=0);
};

Complex::Complex(double r, double i)
{
    real = r;
    imag = i;
}

Complex c1; //error。缺少构造函数的参数
Complex * pc = new Complex; //error。缺少构造函数的参数
Complex c1(2);  //ok
Complex c1(2,4),c2(3,5);
Complex * pc = new Complex(3,4);  
#include<iostream>

class Complex {
    double real, imag;  // private
public:
    Complex(double r, double i);
    Complex(double r);
    Complex(Complex c1, Complex c2);          //多个构造函数是重载关系
};

Complex::Complex(double r, double i)
{
    real = r;
    imag = i;
}

Complex::Complex(double r)
{
    real = r;
    imag = 0;
}

Complex::Complex(Complex c1, Complex c2)
{
    real = c1.real + c2.real;   
    imag = c1.imag + c2.imag;   // 均合法,使用同类对象私有变量
}

int main()
{
    Complex a;      // 非法,无参缺省构造函数已停用
    Complex c1(4), c2(2, 1); // 自动转化为 double
    Complex c3(c1, c2);
    Complex * p = new Complex(1);

    return 0;
}


构造函数与数组:

#include<iostream>

class Test {
public:
    int x;
    Test() 
    {
        std::cout << "Constructor 1 Called." << std::endl;
    }
    Test(int i)
    {
        x = i;
        std::cout << "Constructor 2 Called." << std::endl;
    }
};


int main()
{
    Test A[2];
    std::cout << "Step 1" << std::endl;
    Test B[2] = {4,5};        // A[1]只有一个空元素也要调用constructor
    std::cout << "Step 2" << std::endl;
    Test C[2] = {3};
    std::cout << "Step 3" << std::endl;
    Test * P = new Test[2];   // 这种初始化方式C++11才有,warning: 额外的初始化,new feature
    std::cout << "Step 4" << std::endl;
    delete []P;
  
    return 0;
}


输出

Constructor 1 Called. 
Constructor 1 Called. 
Step 1 
Constructor 2 Called. 
Constructor 2 Called. 
Step 2 
Constructor 2 Called. 
Constructor 1 Called. 
Step 3 
Constructor 1 Called. 
Constructor 1 Called. 
Step 4
#include<iostream>

class Test {
public:
    int x;
    Test() 
    {
        std::cout << "Constructor 1 Called." << std::endl;
    }
    Test(int i)
    {
        x = i;
        std::cout << "Constructor 2 Called." << std::endl;
    }
    Test(int a, int b)
    {
        x = a + b;
        std::cout << "Constructor 3 Called." << std::endl;
    }
};


int main()
{
    Test A[3] = {1,Test{1,2}};        // 三个对象分别用(2),(3),(1)初始化
    std::cout << "Step 1" << std::endl;
    Test B[3] = {Test(2, 1),Test(2, 1), 5}; //三个对象分别用(3),(3),(2)初始化
    std::cout << "Step 2" << std::endl;
    Test * P[3] = {new Test(4), new Test(2,1)};   // p是指针数组,如果没有初始化将不会生成任何对象。
    //用new出来对象的地址来初始化指针。 只初始化两个对象,分别用(2),(3)
    std::cout << "Step 3" << std::endl;
    delete []P;
    Test * PA[3] = {new Test(1), new Test(1,2), NULL};  // PA[2]只有一个指针不调用constructor
    delete PA[1];
    delete PA[0];
    std::cout << "Step 4" << std::endl;

    return 0;
}


输出:

Constructor 2 Called. 
Constructor 3 Called. 
Constructor 1 Called. 
Step 1 
Constructor 3 Called. 
Constructor 3 Called.
Constructor 2 Called. 
Step 2 
Constructor 2 Called. 
Constructor 3 Called. 
Step 3 
Constructor 2 Called. 
Constructor 3 Called. 
Step 4

2. 复制构造函数

构造函数中的一种。


只有一个参数,即对同类对象的引用(可以是

const

或非

const

,一般使用

const

)形如:T :: T ( T & ) 或 T :: T (const T & )

编译器会自动生成复制构造函数,若程序员自己定义,则自动生成的复制构造函数不存在


自动生成的复制构造函数:

#include<iostream>

class A {
public:
    int v;
    A(int i)
    {
        v = i;
    }
};

int main()
{
    A c1(5);
    A c2(c1);   // 缺省的复制构造函数被调用(未定义复制构造函数时)

    std::cout << c1.v << std::endl;
    std::cout << c2.v << std::endl;

    return 0;
}


三种复制构造函数起作用的情况:

  • 用一个对象初始化另一个同类的对象
#include<iostream>

class A {
public:
int v;
A(int i)
{
    v = i;
}
A(A & a)
{
    v = a.v;
    std::cout << "Copy constructor called." << std::endl;
}
};

int main()
{
A c1(5);
A c2(c1);   // 初始化,复制构造函数被调用
A c3 = c2;  // 同样是初始化语句,而非赋值语句,复制构造函数被调用

c1 = c2;    // 赋值语句,不调用复制构造函数
return 0;
}
  • 如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用。
#include<iostream>

class A {
public:
int v;
A(int i)
{
    v = i;
}
A(A & a)
{
    v = a.v;
    std::cout << "Copy constructor called." << std::endl;
}
};

void Fun(A a)
{
std::cout << a.v << std::endl;
}

int main()
{
A c1(5);

Fun(c1);    // 复制构造函数被调用形成形参
return 0;
}


//输出:
//Copy constructor called. 
//5
  • 函数返回值为类的对象,函数返回时,该类的复制构造函数将被调用,初始化返回值
#include<iostream>

class A {
public:
int v;
A(int i)
{
    v = i;
}
A(const A & a)
{
    v = a.v;
    std::cout << "Copy constructor called." << std::endl;
}
};

A Fun(A a)
{
std::cout << a.v << std::endl;
return a;
}

int main()
{
A c1(5);

std::cout << Fun(c1).v << std::endl;    // 复制构造函数被调用形成形参,再被调用形成返回值
return 0;
}

输出:

Copy constructor called. 
5 
Copy constructor called. 
5


Tips

  • 对象间赋值并不导致复制构造函数被调用。
  • 使用

    常量引用参数

    ,避免复制构造函数的调用开销,并避免在函数体内改变被引用的对象。
void fun(const CMyclass & c){
    cout<<"fun"<<endl;
}

3. 析构函数

  • 名字与类名相同,在类名前加



    ~'


    符号
  • 无参数和返回值

  • 一个类最多拥有一个析构函数
  • 析构函数在对象消亡时被调用
  • 缺省的析构函数几乎不进行操作,程序员自定义后缺省的析构函数便消失。


析构函数与数组,指针:

对象数组消亡时,每个对象的析构函数都会被调用

指针所指的动态分配的对象,不delete就不会消亡,也不调用析构函数(new 出来的对象,需使用 delete 才能调用析构函数使其消亡。)

若 new 一个对象数组,那么用 delete 释放时应该写 [ ] 。否则只 delete 一个对象(调用一次析构函数)。

Ctest * pTest;
pTest = new Ctest;  //构造函数调用
delete pTest;       //析构函数调用

pTest = new Ctest[3]; //构造函数调用3次
delete [] pTest;      //析构函数调用3次

构造函数与析构函数调用的例子:

#include <iostream>
using namespace std;

class Demo{
	int id;
public:
	Demo(int i)
	{
		id = i;
		cout<<"id="<<id<<"constructed"<<endl;
	}
	~Demo()
	{
		cout<<"id="<<id<<"destructed"<<endl;
	}
};

Demo d1(1);  //全局变量。在main之前生成,调用构造函数。在main结束后消亡,调用析构函数。
void func()
{
	static Demo d2(2); //静态变量第一次初始化时需调用构造函数,在main结束后消亡,调用析构函数。
	Demo d3(3);         //函数结束就消亡,调用析构函数
	cout<<"func"<<endl;
}

int main()
{
	Demo d4(4);
	d4 = 6;                //调用类型转换构造函数,生成值为6的Demo类的临时对象,赋值后临时对象消亡,调用析构函数。
	cout<<"main"<<endl;
	{
		Demo d5(5);
	}
	func();
	cout<<"main end"<<endl;

	system("pause");
	return 0;
}


输出:

id=1constructed
id=4constructed
id=6constructed
id=6destructed
main
id=5constructed
id=5destructed
id=2constructed
id=3constructed
func
id=3destructed
main end
id=6destructed
id=1destructed
id=2destructed



版权声明:本文为sinat_35483329原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。