1.如果没有构造函数?
面向对象的思想是从生活中来,手机、车出厂时,是一样的。生活中存在的对象都是被初始化后才上市的;初始状态是对象普遍存在的一个状态的 。
如果不用构造函数初始化,该怎么办:
- 为每个类都提供一个public的initialize函数;
- 对象创建后立即调用initialize函数进行初始化。
缺点
1)initialize只是一个普通的函数,必须显示的调用
2)一旦由于失误的原因,对象没有初始化,那么结果将是不确定的没有初始化的对象,其内部成员变量的值是不定的。
#include
using namespace std;
class Test
{
public:
void init(int a, int b)
{
m_a = a;
m_b = b;
}
private:
int m_a;
int m_b;
};
int main(void)
{
Test t1;
int a = 10;
int b = 20;
t1.init(a,b);
Test tArray[3]; //手动调用显示初始化函数
tArray[0].init(0,0);
tArray[1].init(0,0);
tArray[2].init(0,0);
Test t21; //手动调用显示初始化函数
t21.init(0,0);
Test t22;//手动调用显示初始化函数
t22.init(0,0);
Test t23;//手动调用显示初始化函数
t23.init(0,0);//在这种场景之下显示的初 始化方案显得很蹩脚
Test tArray2[3] ={t21,t22,t23};//在这种场景之下,满足不了,编程需要
Test tArray3[1999] ={t21,t22,t23};
return 0;
}
所以C++对类提供了一个给对象的初始化方案,就是构造函数.
2.构造函数
定义:
C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数.
class 类名
{
类名(形式参数){
构造体
}
}
class A
{
A(形参){
}
}
调用:
自动调用:一般情况下C++编译器会自动调用构造函数.
手动调用:在一些情况下则需要手工调用构造函数.
2.1构造函数的分类及调用
class Test
{
public:
//无参数构造函数
Test(){
m_a = 0;
m_b = 0;
p = (char*)malloc(100);
strcpy(p,"abc123");
cout<<"Test()无参构造函数执行"<
规则:
1 在对象创建时自动调用,完成初始化相关工作。
2 无返回值,与类名同,默认无参,可以重载,可默认参数。
3 一经实现,默认不复存在
3.析构函数
定义
C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数.
class 类名
{
~类名(){
析构体
}
}
class A
{
~A()
{}
}
规则:
1 对象销毁时,自动调用。完成销毁的善后工作。
2 无返值 ,与类名同。无参。不可以重载与默认参数
3 析构函数的调用顺序,跟对此昂的构造顺序相反,谁先构造,谁最后一个被析构
析构函数的作用,并不是删除对象,而在对象销毁前完成的一些清理工作。
4.默认的拷贝构造和默认构造函数
class A
{
public:
/*
默认的构造函数
如果普通构造函数,提供了一个显示的构造,那么这个无参的默认构造就会被隐藏,但不会把拷贝构造函数隐藏
A(){
}
*/
/*
默认的拷贝构造函数
A(const A &a){
m_a = a.m_a;
}
*/
//显示的提供一个拷贝构造函数的时候,默认的拷贝构造函数就会被隐藏
//同样,只有提供一个显示的析构函数,就会将默认的析构覆盖掉
private:
int m_a;
};
int main(void)
{
A aObj;//当你不提供任何构造函数的时候,系统会有一个默认的构造函数
A aObj2 = aObj;//调用了aObj2的拷贝构造函数
}
二个特殊的构造函数
1)默认无参构造函数
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
2)默认拷贝构造函数当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函
数,简单的进行成员变量的值复制
5.构造函数规则
- 系统提供默认的拷贝构造器。一经实现,不复存在。
- 系统提供的时等位拷贝,也就是所谓的浅浅的拷贝。
- 要实现深拷贝,必须要自定义。
class Teacher
{
public:
Teacher(int id,char *name){
m_id = id;
//给姓名赋值
int len = strlen(name);
m_name = (char*)malloc(len+1);
strcpy(m_name,name);
}
Teacher(const Teacher &another){
m_id = another.m_id;
int len = strlen(another.m_name);
m_name = (char*)malloc(len+1);
strcpy(m_name,another.m_name);
}
~Teacher(){
//在构造函数中,已经开辟了内存,为了防止泄露
//在析构函数中,在对象销毁之前,把m_name的内存释放掉
if(m_name != NULL){
free(m_name);
m_name = NULL;
cout<<"释放了m_name"<
6.构造函数的初始化列表
如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数
如果没有初始化列表,那么他将无法完成第一步,就会报错。
class A
{
public:
A(int a){
m_a = a;
}
void printA(){
cout<<"a = "<
当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
初始化列表中的初始化顺序,与声明顺序有关,与前后赋值顺序无关。
#include
using namespace std;
class ABCD
{
public:
ABCD(int a, int b, int c)
{
_a = a;
_b = b;
_c = c;
printf("ABCD() construct, a:%d,b:%d,c:%d\n", _a, _b, _c);
}
~ABCD()
{
printf("~ABCD() construct,a:%d,b:%d,c:%d \n", _a, _b, _c);
}
int getA()
{
return _a;
}
private:
int _a;
int _b;
int _c;
};
class MyE
{
public:
MyE() :abcd1(1, 2, 3), abcd2(4, 5, 6), m(100)
{
cout << "MyE()" << endl;
}
MyE(const MyE & obj) :abcd1(7, 8, 9), abcd2(10, 11, 12), m(100)
{
printf("MyE(const MyE() & obj)\n");
}
~MyE()
{
cout << "~MyE()" << endl;
}
public:
ABCD abcd1; //c++编译器不知道如何构造abc1
ABCD abcd2;
const int m;
};
int doThing(MyE mye1) //mye1 = myE //mye1.拷贝构造函数(myE)
{
printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA());
return 0;
}
int run()
{
MyE myE; //调用的无参构造函数
doThing(myE);
return 0;
}
int run2()
{
printf("run2 start..\n");
ABCD(400, 500, 600); //临时对象的⽣命周期 //会产生一个临时的匿名对象。
//再次析构匿名对象 //匿名的临时对象,编译器会立刻销毁。不等到正常函数调用完毕。,
ABCD abcd = ABCD(100, 200, 300);
printf("run2 end\n");
//在此析构abcd
return 0;
}
int main(void)
{
//run();
run2();
return 0;
}