一、析构函数
清理对象占用资源
如使用new
或new[]
分配的内存,用delete
或delete[]
删除动态分配的对象
delete[] _food;
构造函数(Constructor)是C++类的一个特殊成员函数,它的主要作用是初始化对象的状态,确保对象在创建时处于一个有效的初始状态。
构造函数的名称与类名相同,并且没有返回值。
析构函数(Destructor)是C++类的一个特殊成员函数,它的主要作用是在对象生命周期结束时清理对象占用的资源,确保程序的正确性和安全性。
动态分配的对象是指使用 new
或 new[]
运算符在堆(Heap)上分配内存并创建的对象。
与栈(Stack)上分配的局部对象不同,动态分配的对象的生命周期不由作用域控制,而是由程序员显式管理。
这意味着你需要手动使用 delete
或 delete[]
来释放这些对象占用的内存,以避免内存泄漏。
析构函数声明
~Rabbit()
类外定义析构函数
Rabbit::~Rabbit(){}
Rabbit::~Rabbit(){
cout <<""<<endl;
delete[] p;
}
语法结构
类名::~类名() {
// 析构函数体
}
#define _CRT_SECURE_NO_WARNINGS
// 是一个预处理器指令,用于禁用与不安全的C运行时函数相关的警告。虽然它可以减少编译器警告,但建议谨慎使用,优先考虑使用安全版本的函数以避免潜在的安全问题。
#include <cstring> // 需要包含cstring头文件,用于memset和strcpy
#include <iostream>
using namespace std;
class Rabbit{
public:
Rabbit(string name, const char* pf); //构造函数 声明
void eat(); //成员函数
~Rabbit(); //析构函数 声明
private:
string _name;//成变
char* _food;
};
//类外定义构造函数
Rabbit::Rabbit(string name, const char* pf){
cout << "调用构造函数" << endl;
_name = name; //初始化成员变量
_food = new char[50]; // 分配动态内存
memset(_food, 0, 50); //将名为_food的内存区域的前50个字节全部设置为0
strcpy(_food, pf); // 赋值字符串 pf 指向的字符串复制到 _food 指向的内存中
}
//类外定义成员函数
void Rabbit::eat(){
cout << _name << "is eating" << _food << endl;
}
//类外定义析构函数
Rabbit::~Rabbit(){
cout << "调用析构函数" << _name << endl;
if (_food!=NULL)
delete[] _food;
}
int main(){
Rabbit A("A", "luobo");
A.eat();
Rabbit B("B", "baicai");
B.eat();
return 0;
}
输出
二、拷贝构造函数
拷贝构造函数(Copy Constructor)是C++中的一个特殊构造函数,
用于创建一个新对象作为现有对象的副本。
它的语法结构通常如下:
类名(const 类名& 参数名);
class(const className& other);
#include<iostream>
#include<string>
using namespace std;
class Sheep{
public:
Sheep(string name, string color);
Sheep(const Sheep& other);
void show() const; // 添加了 const 修饰符,表示该方法不会修改对象状态
~Sheep();
private:
string _name;
string _color;
};
// 构造函数
Sheep::Sheep(string name, string color){
cout << "调用构造函数" << endl;
_name = name;
_color = color;
}
// 拷贝构造函数
Sheep::Sheep(const Sheep& another){
cout << "调用拷贝构造函数" << endl;
_name = another._name;
_color = another._color;
}
void Sheep::show() const {
cout << _name << " " << _color << endl;
}
// 析构函数
Sheep::~Sheep(){
cout << "调用析构函数" << endl;
}
int main(){
Sheep sheepA("Doly", "white");
cout << "sheepA: ";
sheepA.show();
Sheep sheepB(sheepA);
cout << "sheepB: ";
sheepB.show();
return 0;
}
输出结果
以张三学习成绩为例写一个拷贝构造函数代码 (1)构造函数体内赋值法 类名::类名(类型名1 参数名1, 类型名2 参数名2){ 成员变量1=参数名1; 成员变量2=参数名2; } Student::Student(string name, double score){ _name = name; _score = score; }
#include<iostream>
#include<string>
using namespace std;
//含有构造函数、拷贝构造函数、析构函数的类
class Student{
public:
Student(string name, double score);
Student(const Student& other);
void show();
~Student();
private:
string _name;
double _score;
};
//类外定义构造函数
Student::Student(string name, double score){
cout << "构造函数" << endl;
_name = name;
_score = score;
}
//类外定义拷贝构造函数
Student::Student(const Student& other){
cout << "拷贝构造函数" << endl;
_name = other._name;
_score = other._score;
}
//类外定义成员函数
void Student::show(){
cout << _name << " " << _score << endl;
}
//类外定义析构函数
Student::~Student(){
cout << "析构函数" << endl;
}
int main(){
Student stuA("张三", 99.5);
stuA.show();
Student stuB("张三", 99.5);
stuB.show();
return 0;
}
构造函数
张三 99.5
拷贝构造函数
张三 99.5
析构函数
析构函数
(2)成员初始化列表法
类名::类名(类型名1 参数名1, 类型名2 参数名2): 初始化成员变量{
}
类名::类名(类型名1 参数名1, 类型名2 参数名2): 成员变量1(参数1), 成员变量2(参数2){
}
Student::Student(string name, double score):_name(name), _score(score){
}
#include <iostream>
#include <string>
using namespace std;
class Student {
public:
// 构造函数使用成员初始化列表(尽管在这里不是必需的,因为成员变量是简单类型)
Student(string name, double score) : _name(name), _score(score) {
cout << "构造函数" << endl;
}
// 拷贝构造函数
Student(const Student& other) {
cout << "拷贝构造函数" << endl;
_name = other._name;
_score = other._score;
}
// show方法不使用const
void show() {
cout << _name << " " << _score << endl;
}
// 析构函数
~Student() {
cout << "析构函数" << endl;
}
private:
string _name;
double _score;
};
int main() {
// 创建第一个Student对象
Student stuA("张三", 99.5);
stuA.show();
// 这里实际上调用的是构造函数,不是拷贝构造函数
// 如果想观察拷贝构造函数的调用,可以创建一个临时对象,然后通过赋值或传递来触发拷贝构造
// 例如:
// Student temp("李四", 88.0);
// Student stuB = temp; // 这里会调用拷贝构造函数
// 或者通过函数返回对象等方式
// 但为了保持示例简单,我们再次调用构造函数创建一个新对象
Student stuB("李四", 88.0);
stuB.show();
// 注意:在main函数结束时,stuA和stuB的析构函数会被自动调用
// 输出顺序可能是stuB的析构,然后是stuA的析构,这取决于编译器的实现和对象的销毁顺序
return 0;
}
三、浅拷贝
浅拷贝是指在对象复制过程中,仅复制对象的直接成员变量,而不递归复制成员变量所指向的内容。
如果成员变量是指针或引用,浅拷贝只是复制指针或引用本身,而不是它们所指向的实际数据。
特点:
-
只复制一层:只复制对象的直接成员,不涉及成员的成员。
-
共享底层数据:如果成员变量是指针或引用,浅拷贝后,多个对象会共享同一块底层数据。
-
节省内存:因为不复制底层数据,所以内存占用少,速度也更快。
优点:
-
性能高,因为不需要复制大量数据。
-
简单易实现,通常直接赋值即可。
缺点:
-
如果底层数据被多个对象共享,可能会导致数据一致性问题。
-
在析构时容易出现双重释放问题,因为多个对象可能尝试释放同一块内存。
基础课笔记:
简单赋值,堆内数值不能拷贝
成员变量是指针或引用,浅拷贝只简单指针赋值,指向旧的地址,并没有为新对象申请新空间。
下面代码,在前面代码基础上添加了指针char* _home成员变量
操作:
类外定义(实现)构造函数时,
构造函数内部为_home申请了堆内空间
strcpy将 形参home 数据给了成员变量_home
//为指针成员home分配空间, 将形参home的内容复制到_home指向的空间
//浅拷贝
int len = strlen(home) + 1;
_home = new char[len];
memset(_home, 0 len);
strcpy(_home, home);
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
class Sheep{// 定义羊类
public:
Sheep(string name, string color, char* home); // 声明有参构造函数
Sheep(const Sheep& other); //声明拷贝构造函数
void show(); // 声明成员函数
~Sheep(); //声明析构函数
private:
string _na