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