构造函数与析构函数
概要
学习构造析构函数,完成对象数据成员的初始化和清理工作。
普通构造函数 拷贝构造函数(**)
析构函数
普通构造函数
是一种特殊的成员函数,主要用来在创建对象时初始化对象——为对象的成员变量赋初始值;
构造函数名和类名相同;
无返回值类型(void 也是一种返回值类型),也没有返回值;
构造函数函数名是确定的,但是参数表可以改变(可使用函数重载);
构造函数不能主动调用,在创建一个新的对象的时候会自动调用。如果一个类中没有显式的给出构造函数,系统会自动地给出一个构造函数(默认给的构造函数也为公有属性);
对于有参构造函数,在创建对象是传入参数即可;
MyClass obj(1,2); // 有参构造传参方法
代码:
#include <iostream>
using namespace std;
class MyClass
{
public:
int num;
void setVal(int v);
int getVal(void);
private:
int val;
public:
MyClass();
MyClass(int n, int v = 0);
};
MyClass::MyClass(int n,int v)
{
cout << "MyClass(int n, int v)" << endl;
num = n;
val = v;
}
MyClass::MyClass()
{
cout << "MyClass()" << endl;
}
void MyClass::setVal(int v)
{
val = v;
}
int MyClass::getVal(void)
{
return val;
}
int main()
{
MyClass obj_0;
MyClass obj_1(1, 2);
MyClass obj_2(9);
cout << obj_2.getVal() << endl;
MyClass* p_0 = new MyClass;
MyClass* P_1 = new MyClass(10, 12);
return 0;
}
注1:C语言中 const 修饰的变量具有只读属性,不能直接修改,而在C++中会将 const 修饰的变量改变性质成为一个常量。
注2:有时数据必须初始化,不能在构造函数中赋值,可以用成员初始化列表的方式给数据成员复制。
代码:
#include <iostream>
using namespace std;
class MyClass
{
public:
const int id;
const int num;
const int val;
public:
MyClass(int i,int n,int v);
};
// 成员初始化列表:只在定义时初始化,声明时不要加初始化列表。
MyClass::MyClass(int i, int n,int v):id(i), num(n), val(0)
{
}
int main()
{
MyClass obj_0(1,2,3);
MyClass obj_1(4,5,6);
cout << obj_0.id << endl;
cout << obj_1.id << endl;
return 0;
}
析构函数
也是一种特殊函数,主要作用是在对象生命周期结束时进行清理,系统可以自动调用析构函数。
函数名与类名相同,在前面加上一个 ~(C/C++中唯一一个函数名不是由合法标识符组成的函数);
没有返回值类型和返回值,也没有参数;
不写时系统会给个默认的。
析构可以主动通过对象调用,析构必须是公有属性下;
在对象生命周期结束时,会自动调用析构函数。
不是因为调用了析构函数导致对象的生命周期结束,是对象的生命周期结束时,会自动调用析构函数。
代码:
#include <iostream>
using namespace std;
class MyClass
{
public:
int id;
MyClass();
~MyClass();
};
MyClass::MyClass()
{
cout << "构造" << endl;
}
MyClass::~MyClass()
{
cout << "析构" << endl;
}
int main()
{
MyClass obj_0;
obj_0.~MyClass();
return 0;
}
拷贝构造函数
拷贝构造函数是一种特殊的构造函数,通过拷贝构造完成一个复制的过程(从一个对象得到另外的一个对象,对象名不同,数据相同),读作:拷贝构造,函数。
特殊:第一个参数(必须)是本类的对象的引用。
语法:先是构造函数,才可能是拷贝构造函数,没写时系统自动生产,将成员一一对应赋值。
代码示例:
#include <iostream>
using namespace std;
class MyClass
{
public:
// 默认构造函数
MyClass() {}
// 默认析构函数
~MyClass() {}
// 默认拷贝构造函数
MyClass(const MyClass& obj) {}
// 是拷贝构造函数,因为第一个参数是第一个对象的引用
MyClass(const MyClass& obj,int n) {}
// 不是拷贝构造函数,因为第一个参数不是第一个对象的引用
MyClass(int n, const MyClass& obj) {}
};
int main()
{
return 0;
}
示例:
#include <iostream>
#include <string>
using namespace std;
class Monster
{
public:
string m_name;
int m_hp;
float m_speed;
public:
Monster();
Monster(const char* name, int hp, float speed);
Monster(Monster& obj);
Monster(Monster& obj, int val);
~Monster();
void showData();
};
Monster::Monster()
{
cout << "无参构造" << endl;
m_name = "名字";
m_hp = 100;
m_speed = 10.0f;
}
Monster::Monster(const char* name, int hp, float speed)
{
cout << "带参构造" << endl;
m_name = name;
m_hp = hp;
m_speed = speed;
}
Monster::Monster(Monster& obj)
{
cout << "拷贝构造 1 号" << endl;
// 默认拷贝构造函数的功能是将传入对象的成员值传参到新对象的成员中。
m_name = obj.m_name;
m_hp = obj.m_hp;
m_speed = obj.m_speed;
}
Monster::Monster(Monster& obj, int val)
{
cout << "拷贝构造 2 号" << endl;
m_name = obj.m_name;
m_hp = val;
m_speed = obj.m_speed;
}
Monster::~Monster()
{
cout << "析构" << endl;
}
void Monster::showData()
{
cout << "角色名称:" << m_name << endl;
cout << "当前血量:" << m_hp << endl;
cout << "移动速度:" << m_speed << endl;
cout << "************************" << endl << endl;
}
int main()
{
Monster m0;
Monster m1("皮卡丘",120, 6.5f);
Monster m2 = m0; // 当使用一个对象给另一个对象赋值时,会调用拷贝构造函数。
Monster m3(m0); // 会调用拷贝构造
Monster m4(m1, 180); // 会调用拷贝构造
Monster* p = new Monster; // 不会主动释放(不会调用析构)
delete p; // 释放 p 所指向的内存 (调用析构函数)
p = NULL; // p 指向 空
m0.showData();
m1.showData();
m2.showData();
m3.showData();
m4.showData(); // 先构造的后析构
// p->showData();
return 0;
}
拷贝构造调用时机:
- 使用一个对象给另一个对象初始化;
- 使用一个对象构造另一个对象;
- 函数的参数是类的对象;(示例 3)
- 函数类的返回值是类的对象(我的编译器未成功调用)
// 示例3:
#include <iostream>
#include <string>
using namespace std;
class Monster
{
public:
string m_name;
int m_hp;
float m_speed;
public:
Monster();
Monster(const char* name, int hp, float speed);
Monster(Monster& obj);
Monster(Monster& obj, int val);
~Monster();
void showData();
};
Monster::Monster()
{
cout << "无参构造" << endl;
m_name = "名字";
m_hp = 100;
m_speed = 10.0f;
}
Monster::Monster(const char* name, int hp, float speed)
{
cout << "带参构造" << endl;
m_name = name;
m_hp = hp;
m_speed = speed;
}
Monster::Monster(Monster& obj)
{
cout << "拷贝构造 1 号" << endl;
// 默认拷贝构造函数的功能是将传入对象的成员值传参到新对象的成员中。
m_name = obj.m_name;
m_hp = obj.m_hp;
m_speed = obj.m_speed;
}
Monster::Monster(Monster& obj, int val)
{
cout << "拷贝构造 2 号" << endl;
m_name = obj.m_name;
m_hp = val;
m_speed = obj.m_speed;
}
Monster::~Monster()
{
cout << "析构" << endl;
}
void Monster::showData()
{
cout << "角色名称:" << m_name << endl;
cout << "当前血量:" << m_hp << endl;
cout << "移动速度:" << m_speed << endl;
cout << "************************" << endl << endl;
}
void func1(Monster obj)
{
}
int main()
{
Monster m0;
Monster m1("皮卡丘",120, 6.5f);
func1(m0);
return 0;
}
运行结果:
深拷贝与浅拷贝
浅拷贝:默认的都是浅拷贝;
深拷贝:需要根据实际情况实现。
eg: 对于类的对象中有指针变量时,如果使用默认的拷贝构造函数,则新创建对象的指针只会拷贝原对象中指针指向的地址,而不会开辟新的内存空间拷贝原对象指针指向地址的值,此时需要修改默认的拷贝构造函数。
#include <iostream>
#include <string>
using namespace std;
class Monster
{
public:
string m_name;
int m_hp;
float m_speed;
int* p;
public:
Monster();
Monster(const char* name, int hp, float speed);
Monster(Monster& obj);
Monster(Monster& obj, int val);
~Monster();
};
Monster::Monster()
{
cout << "无参构造" << endl;
m_name = "名字";
m_hp = 100;
m_speed = 10.0f;
p = NULL;
}
Monster::Monster(const char* name, int hp, float speed)
{
cout << "带参构造" << endl;
m_name = name;
m_hp = hp;
m_speed = speed;
p = new int[10];
}
Monster::Monster(Monster& obj)
{
cout << "拷贝构造 1 号" << endl;
// 默认拷贝构造函数的功能是将传入对象的成员值传参到新对象的成员中。
m_name = obj.m_name;
m_hp = obj.m_hp;
m_speed = obj.m_speed;
p = obj.p;
}
Monster::Monster(Monster& obj, int val)
{
cout << "拷贝构造 2 号" << endl;
m_name = obj.m_name;
m_hp = val;
m_speed = obj.m_speed;
p = new int[10];
for (size_t i = 0; i < 10; i++)
{
p[i] = obj.p[i];
// *(p + i) = *(obj.p + i); // 第二种写法
}
}
Monster::~Monster()
{
cout << "析构" << endl;
if (p != NULL)
{
delete p;
p = NULL;
}
}
void func1(Monster obj)
{
}
int main()
{
Monster m0("你好",110,2.5f);
m0.p[0] = 1;
Monster m1(m0);
cout << m1.p[0] << endl;
// 会报错,原因是 m0.p m1.p 都指向相同的地址,
// 程序先执行 m1 的析构时释放掉 m1.p 所指向的内存
// 执行 m0.p 的析构时,该内存已释放,报错。
return 0;
}
执行如下代码时,不会报错:
#include <iostream>
#include <string>
using namespace std;
class Monster
{
public:
string m_name;
int m_hp;
float m_speed;
int* p;
public:
Monster();
Monster(const char* name, int hp, float speed);
Monster(Monster& obj);
Monster(Monster& obj, int val);
~Monster();
};
Monster::Monster()
{
cout << "无参构造" << endl;
m_name = "名字";
m_hp = 100;
m_speed = 10.0f;
p = NULL;
}
Monster::Monster(const char* name, int hp, float speed)
{
cout << "带参构造" << endl;
m_name = name;
m_hp = hp;
m_speed = speed;
p = new int[10];
}
Monster::Monster(Monster& obj)
{
cout << "拷贝构造 1 号" << endl;
// 默认拷贝构造函数的功能是将传入对象的成员值传参到新对象的成员中。
m_name = obj.m_name;
m_hp = obj.m_hp;
m_speed = obj.m_speed;
p = obj.p;
}
Monster::Monster(Monster& obj, int val)
{
cout << "拷贝构造 2 号" << endl;
m_name = obj.m_name;
m_hp = val;
m_speed = obj.m_speed;
p = new int[10];
for (size_t i = 0; i < 10; i++)
{
p[i] = obj.p[i];
// *(p + i) = *(obj.p + i); // 第二种写法
}
}
Monster::~Monster()
{
cout << "析构" << endl;
if (p != NULL)
{
delete p;
p = NULL;
}
}
void func1(Monster obj)
{
}
int main()
{
Monster m0("你好",110,2.5f);
m0.p[0] = 1;
Monster m1(m0, 120);
cout << m1.p[0] << endl;
return 0;
}
<未完待续>