一、构造函数
1. 构造函数定义及调用:
C++中的类可以定义在与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数
构造函数在定义时可以有参数
没有任何返回类型的声明
调用:
自动调用:一般情况下c++编译器会自动调用构造函数
手动调用:在一些情况下则需要手工调用构造函数
2. 有关析构函数
在C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数就叫做析构函数
语法:
~ClassName()
析构函数也没有参数也没有任何返回类型的声明
析构函数在对象销毁的时候会自动调用
二、拷贝构造函数
拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
1. 什么时候用拷贝构造函数:当用一个已经初始化过了的自定义类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用(即在程序中建立对象时被调用)。也就是说,当类的对象需要拷贝的时,拷贝构造函数将会被调用。一下情况会调用拷贝构造函数:
1)一个对象以值传递方式传入函数体(当函数的参数为类的对象时)
2)一个对象以值传递的方式从函数返回(函数的返回值是类的对象)
3)一个对象需要通过另外一个对象进行初始化
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。
例子:
#include <iostream>
using namespace std;
class Test4
{
public:
Test4() // 无参数构造函数
{
m_a = 0;
m_b = 0;
cout<<"无参数构造函数"<<endl;
}
Test4(int a)
{
m_a = a;
m_b = 0;
}
Test4(int a, int b) // 有参数构造函数
{
m_a = a;
m_b = b;
cout<<"有参数构造函数"<<endl;
}
// 赋值构造函数 (copy构造函数) //
Test4(const Test4& obj )
{
cout<<"我也是构造函数 " <<endl;
m_b = obj.m_b + 100;
m_a = obj.m_a + 100;
}
public:
void print()
{
cout<<"普通成员函数"<<endl;
cout<<"m_a"<<m_a<<" m_a"<<m_b<<endl;
}
private:
int m_a;
int m_b;
};
void main()
{
// 无参构造函数
Test4 t0;
// Test4(); 与上面的一样
cout << endl;
Test4 t1(1, 2);
cout << endl;
Test4 t2 = t1; // 初始化
Test4 t3(t1);
Test4 t4 = Test4 (t2);
system("pause");
}
拷贝构造函数场景:
class Location
{
public:
Location( int xx = 0 , int yy = 0 )
{
X = xx ;
Y = yy ;
cout << "Constructor Object.....\n" ;
}
// copy构造函数 完成对象的初始化
Location(const Location & obj) // copy构造函数
{
X = obj.X;
Y = obj.Y;
cout << "采用对象初始化调用" << endl;
}
~Location()
{
cout << X << "," << Y << " Object destroyed." << endl ;
}
int GetX ()
{
return X ;
}
int GetY ()
{
return Y ;
}
private :
int X , Y ;
} ;
void f(Location p)
{
cout << p.GetX() << endl;
}
void main0301()
{
Location a(1, 2);
cout << "第一次调用完成" << endl;
Location b = a;
cout << "第二次调用完成" << endl;
cout<<"b对象已经初始化完毕"<<endl;
f(b);
system("pause");
}
注意:拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。
1> 拷贝构造函数首先是一个构造函数,它调用的时候产生一个对象,是通过参数传进来的那个对象来初始化,产生的对象。
2> operator=();是把一个对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。
3> 还要注意的是拷贝构造函数是构造函数,不返回值;而赋值函数需要返回一个对象自身的引用,以便赋值之后的操作。
三、构造函数的初始化列表
1. 对象初始化列表出现的原因:
1)必须这样做:
如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。
2)类成员中若有const修饰,必须在对象初始化的时候,给const int m 赋值
当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,
因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
#include <iostream>
using namespace std;
class A
{
public:
A(int _a)
{
m_a = _a;
cout << "A类有参构造函数" << endl;
}
~A()
{
cout << "析构函数" << "a = " << m_a << endl;
}
private:
int m_a;
};
// 定义类B
// 构造函数的初始化列表 解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
// 新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
class B
{
public:
B(int _b1, int _b2) : m_a1(0), m_a2(1),c(5)
{
m_b1 = _b1;
m_b2 = _b2;
cout << "B类有参构造函数" << endl;
}
B(int _b1, int _b2, int m, int n) : m_a1(m), m_a2(n), c(0)
{
m_b1 = _b1;
m_b2 = _b2;
cout <<"B的构造函数"<<endl;
}
~B()
{
cout<<"B的析构函数" <<endl;
}
private:
int m_b1;
int m_b2;
A m_a2; // 2 比 1 先执行
A m_a1;
const int c ;
};
void main0401()
{
// A(1);
B(2, 3);
B(1, 2, 3, 4);
system("pause");
return ;
}
注意:
成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关。初始化列表先于构造函数的函数体执行
当类中有成员变量是其它类的对象时,首先调用成员变量的构造函数,调用顺序与生命顺序相同;之后调用自身类的构造函数
析构函数的调用顺序与对应的构造函数调用顺序相反