构造和析构函数
1、前言
创建一个对象时常常需要做某些初始化工作,例如对数据成员赋初值,
注意:类的数据成员是不能在声明类的时候初始化的
为了解决这个问题,C++编译器提供了构造函数来处理对象的初始化,构造函数是一种特殊的成员函数,与其它成员函数不同,不需要用户来调用它,而是在建立对象是自动执行。
2、构造和析构函数
有关构造函数
1构造函数定义及调用
1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
2)构造函数在定义时可以有参数;
3)没有任何返回类型的声明。
2构造函数的调用
自动调用:一般情况下C++编译器会自动调用构造函数
手动调用:在一些情况下则需要手工调用构造函数
有关析构函数
3)析构函数定义及调用
1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用
4)析构函数调用机制
C++编译器自动调用
#include <iostream>
using namespace std;
class Test {
public:
Test()//无参数 无返回值
{
cout << "我是构造函数,被调用了" << endl;
}
~Test()
{
cout << "我是析构函数,被执行了" << endl;
}
};
//给对象一个舞台,让对象被调用
void objplay()
{
Test t1;
}
int main()
{
objplay();
system("pause");
return 0;
}
3、C++编译器构造析构方案 PK 对象显示初始化方案
设计构造函数和析构函数的原因
面向对象的思想是从生活中来,手机、车出厂时,是一样的。
生活中存在的对象都是被初始化后才上市的;初始状态是对象普遍存在的一个状态的
普通方案:
为每个类都提供一个public的initialize函数;
对象创建后立即调用initialize函数进行初始化。
优缺点分析
1)initialize只是一个普通的函数,必须显示的调用
2)一旦由于失误的原因,对象没有初始化,那么结果将是不确定的
没有初始化的对象,其内部成员变量的值是不定的
3)不能完全解决问题
4、构造函数的分类及调用
/* 构造函数的分类和调用 */
class Test2 {
public:
Test2()//无参数 无返回值
{
m_a = 0;
m_b = 0;
cout << "无参数构造函数" << endl;
}
Test2(int a)//无参数 无返回值
{
m_a = a;
m_b = 0;
cout << "一个参数的构造函数" << endl;
}
Test2(int a,int b)// 无返回值
{
m_a = a;
m_b = b;
cout << "两个参数的构造函数" << endl;
}
//赋值构造函数(拷贝构造函数)
Test2(const Test2& obj)// 无返回值
{
cout << "我也是构造函数" << endl;
}
~Test2()
{
cout << "我是析构函数,被执行了" << endl;
}
public:
void printT()
{
cout << "普通成员函数 " << endl;
}
private:
int m_a;
int m_b;
};
int main()
{
//Test2 t1; //默认调用无参数的构造函数
/* 调用有参构造函数 有三种方法 */
//方法1 括号法 必须加上参数 C++编译器自动调用
//Test2 t1(1, 2);
//方法2
//Test2 t1 = (3, 4); //调用的是一个参数的构造函数 因为等号右边是一个等号表达式值是4 C++编译器自动调用
//t1.printT();
//方法3 直接手动调用构造函数
Test2 t1 = Test2(3, 4);
system("pause");
return 0;
}
5、赋值构造函数(copy构造函数)的调用时机
第一二种调用时机
#include <iostream>
using namespace std;
class Test2 {
public:
Test2()//无参数 无返回值
{
m_a = 0;
m_b = 0;
cout << "无参数构造函数" << endl;
}
Test2(int a)//无参数 无返回值
{
m_a = a;
m_b = 0;
cout << "一个参数的构造函数" << endl;
}
Test2(int a, int b)// 无返回值
{
m_a = a;
m_b = b;
cout << "两个参数的构造函数" << endl;
}
//赋值构造函数(拷贝构造函数)
Test2(const Test2& obj)// 无返回值
{
cout << "我也是构造函数" << endl;
m_b = obj.m_b + 100;
m_a = obj.m_a + 100;
}
~Test2()
{
cout << "我是析构函数,被执行了" << endl;
}
public:
void printT()
{
cout << "普通成员函数 " << endl;
cout << "m_a " << m_a << " m_b " << m_b << endl;
}
private:
int m_a;
int m_b;
};
/* 赋值构造函数 用一个对象去初始化另一个对象 */
int main()
{
Test2 t1(1, 2);
Test2 t0(1, 2);
/* 第一种调用时机 */
//Test2 t2 = t1; // =操作会调用t2的拷贝构造函数,用t1来初始化t2
//t0 = t1; //用t1给t0赋值 两个=的意义不同 赋值操作和初始化是两个不同的概念 赋值操作是不会调用拷贝构造函数的
/* 第二种调用时机 */
Test2 t2(t1); //直接用t1初始化t2 这种方法是直接调用t2的拷贝构造函数
t2.printT();
system("pause");
return 0;
}
第三种调用时机
#include "iostream"
using namespace std;
class Location
{
public:
Location(int xx = 0, int yy = 0)
{
X = xx; Y = yy; cout << "Constructor Object.\n";
}
//复制构造函数 完成对象的初始化
Location(const Location & p)
{
X = p.X; Y = p.Y;
cout << "Copy_constructor called." << 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 << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}
void mainobjplay()
{
Location a(1, 2);
Location b = a; //会调用b的拷贝构造函数 用a来初始化b
cout << "b对象已经初始化完毕" << endl;
f(b);//用实参b去初始化形参p 此时会调用拷贝构造函数 一共调用了3次构造函数,最后调用3次析构函数
}
void main()
{
/* 第三种调用时机 */
mainobjplay();
system("pause");
}
第四种调用时机
#include "iostream"
using namespace std;
class Location
{
public:
Location(int xx = 0, int yy = 0)
{
X = xx; Y = yy; cout << "Constructor Object.\n";
}
//复制构造函数 完成对象的初始化
Location(const Location & p)
{
X = p.X; Y = p.Y;
cout << "Copy_constructor called." << 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 << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}
void mainobjplay()//第三种
{
Location a(1, 2);
Location b = a; //会调用b的拷贝构造函数 用a来初始化b
cout << "b对象已经初始化完毕" << endl;
f(b);//用实参b去初始化形参p 此时会调用拷贝构造函数 一共调用了3次构造函数,最后调用3次析构函数
}
//业务函数 第四种调用时机用 返回一个元素
//结论1:函数的返回值是一个元素(复杂数据类型,返回的是一个新的匿名对象,所以会调用匿名对象类的拷贝构造函数
//结论2:有关匿名对象的去和留:关键看,返回时如何接
//如果用匿名对象初始化另一个同类型的对象,那么匿名对象直接转成这个有名对象
//如果用匿名对象赋值给另外一个同类型的对象,那么匿名对象会马上被析构
Location g()
{
Location A(1, 3);
//当返回一个类的对象时,会自动调用拷贝构造函数创建一个匿名对象
return A;
}
//第四种调用时机的第一个场景
void objplay()
{
g();
}
//第四种调用时机的第二个场景
void objplay2()
{
//用匿名对象初始化m 此时C++编译器直接把这个匿名对象转成m (扶正 从匿名转有名)
Location m = g();
printf("匿名对象被扶正,不会被析构\n");
cout << m.GetX() << endl;
}
void objplay3()
{
//用匿名对象初始化m 此时C++编译器直接把这个匿名对象转成m (扶正 从匿名转有名)
Location m2(1, 2);
m2 = g();
printf("此时因为用匿名对象=给m2,匿名对象将会被析构\n");
cout << m2.GetX() << endl;
}
void main()
{
//第四种调用时机
//objplay2();
objplay3();
system("pause");
}
总结
上一篇: 唐玄宗抢李瑁的妻子,李瑁反抗过吗?
下一篇: 构造函数和析构函数