在C++中,构造函数和析构函数是类的重要成员函数,分别用于对象的创建和销毁。它们帮助自动初始化和清理对象的资源,避免内存泄漏和未初始化的问题。
ClassName() {
// 初始化代码
}
void
。public
权限,以便外部能够调用。void
。~ClassName() {
// 清理代码
}
ClassName
是类的名称,前面加上波浪号(~
)表示它是析构函数。void
)。在 C++ 中,析构函数没有种类的区分,但它可以在不同的上下文中发挥不同的作用:
普通析构函数:这是默认析构函数,当对象销毁时会自动调用,用于释放资源或进行清理。
例如:
class MyClass {
public:
~MyClass() {
cout << "析构函数调用" << endl;
}
};
虚析构函数:当类设计为基类并且可能会通过基类指针删除派生类对象时,需要使用虚析构函数。虚析构函数确保派生类的析构函数也会被调用。
例如:
class Base {
public:
virtual ~Base() { // 虚析构函数
cout << "Base 类析构函数" << endl;
}
};
class Derived : public Base {
public:
~Derived() override {
cout << "Derived 类析构函数" << endl;
}
};
这样,当基类指针指向派生类对象并且 delete
该指针时,派生类的析构函数将被调用,确保正确释放资源。
析构函数的主要功能是清理和释放对象在其生命周期中占用的资源,常见的功能包括:
new
分配的内存,需要通过析构函数释放。delete
或 delete[]
释放堆内存时,析构函数会被调用。()
来明确指定参数并调用构造函数。class MyClass {
public:
MyClass(int x) {
cout << "构造函数调用,x = " << x << endl;
}
};
int main() {
MyClass obj(10); // 括号法调用构造函数
return 0;
}
输出:
构造函数调用,x = 10
在此例中,MyClass obj(10);
是括号法构造函数调用,它会直接调用带一个整数参数的构造函数。
=
进行初始化。这种方式可以用于使用构造函数创建对象,特别是当你从一个已有对象创建新对象时,编译器会通过显示法来调用拷贝构造函数。=
调用构造函数,适用于创建对象时,通常会发生类型转换或拷贝构造。class MyClass {
public:
MyClass(int x) {
cout << "构造函数调用,x = " << x << endl;
}
};
int main() {
MyClass obj = 10; // 显示法调用构造函数
return 0;
}
输出:
构造函数调用,x = 10
在此例中,MyClass obj = 10;
看似是赋值操作,但实际上它会调用构造函数来初始化 obj
。显示法在 C++ 中会有隐式转换行为,例如上例中 10
被转换成 MyClass
类型,调用带有参数的构造函数。
class MyClass {
public:
MyClass(double x) {
cout << "构造函数调用,x = " << x << endl;
}
};
int main() {
MyClass obj = 10; // 隐式转换法调用构造函数
return 0;
}
输出:
构造函数调用,x = 10
在此例中,MyClass obj = 10;
通过隐式转换调用了接受 double
参数的构造函数。虽然传递的是 int
类型,但编译器会将 int
自动转换为 double
类型,然后调用构造函数。
类名(构造函数实参表):成员1(初值1),成员2(初值2)
{
构造函数函数体;
}
ClassName(Type arg1, Type arg2) : member1(arg1), member2(arg2) {
// 构造函数体
}
member1(arg1)
:表示将构造函数的参数 arg1
用来初始化成员变量 member1
。member2(arg2)
:表示将构造函数的参数 arg2
用来初始化成员变量 member2
。#include
using namespace std;
class MyClass {
private:
int x;
double y;
public:
// 构造函数,使用初始化构造列表来初始化成员变量
MyClass(int val1, double val2) : x(val1), y(val2) {
cout << "构造函数被调用" << endl;
}
// 打印成员变量
void print() {
cout << "x = " << x << ", y = " << y << endl;
}
};
int main() {
MyClass obj(10, 20.5); // 初始化构造列表初始化成员变量
obj.print(); // 打印结果
return 0;
}
输出:
构造函数被调用
x = 10, y = 20.5
场景1:当形参列表 和 成员变量 名字重名的情况下
可以使用初始化表 或者 this指针 解决
场景2:当 成员变量 中 有引用类型的 成员时
这个时候 必须使用 初始化列表
类名(type & num):val(num);
场景3: 当类中有const 修饰的成员变量时
场景4: 当类中有成员子对象时
类中 的 成员子对象 的有参构造
当类中有成员子对象时,需要在构造函数的初始化中调用成员子对象的构造函数
并传参完成对成员子对象初始化,如果没有调用成员子对象的构造函数
默认会调用成员子对象的无参构造函数 但是如果 成员子对象 没有无参构造 则会报错
在 C++ 中,拷贝构造函数(Copy Constructor)是一个特殊的构造函数,它用于创建一个新的对象,并将另一个同类型对象的值复制到这个新对象中。拷贝构造函数通常在以下几种情况下被调用:
浅拷贝是指在拷贝构造函数中,仅复制对象的每个成员变量的值。如果某个成员是指针,浅拷贝会直接复制指针的地址,而不是指针所指向的对象。
ClassName(const ClassName &other) {
data = other.data; // 直接复制指针的值(地址)
}
浅拷贝通常在以下情况下使用:
int
, double
等),或者没有动态分配的资源。#include
using namespace std;
class ShallowCopyExample {
private:
int *data;
public:
// 构造函数
ShallowCopyExample(int value) {
data = new int(value);
}
// 浅拷贝构造函数
ShallowCopyExample(const ShallowCopyExample &other) {
data = other.data; // 只复制指针,不是数据的副本
}
// 析构函数
~ShallowCopyExample() {
delete data;
}
void print() {
cout << "data = " << *data << endl;
}
};
int main() {
ShallowCopyExample obj1(10); // 创建 obj1
ShallowCopyExample obj2 = obj1; // 浅拷贝构造函数调用
obj2.print(); // 输出 obj2 的数据
return 0;
}
输出:
data = 10
深拷贝是指在拷贝构造函数中,不仅复制对象的成员变量值,而且还为指针成员分配新的内存空间,复制指针指向的内容。这样,源对象和目标对象将拥有各自独立的内存。
ClassName(const ClassName &other) {
data = new int(*other.data); // 分配新内存并复制数据
}
深拷贝通常用于以下情况:
#include
using namespace std;
class DeepCopyExample {
private:
int *data;
public:
// 构造函数
DeepCopyExample(int value) {
data = new int(value);
}
// 深拷贝构造函数
DeepCopyExample(const DeepCopyExample &other) {
data = new int(*other.data); // 为新对象分配内存并复制数据
}
// 析构函数
~DeepCopyExample() {
delete data;
}
void print() {
cout << "data = " << *data << endl;
}
};
int main() {
DeepCopyExample obj1(10); // 创建 obj1
DeepCopyExample obj2 = obj1; // 深拷贝构造函数调用
obj2.print(); // 输出 obj2 的数据
return 0;
}
输出:
data = 10
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
拷贝行为 | 只复制对象的成员变量值,指针成员复制的是地址。 | 不仅复制成员变量值,还为指针成员分配新的内存并复制数据。 |
内存管理 | 没有为指针成员分配新的内存,多个对象共享相同的资源。 | 为每个对象分配独立的内存,确保每个对象独立管理资源。 |
风险 | 可能出现双重释放(double free)或悬挂指针(dangling pointer)。 | 可能导致内存泄漏(memory leak)或性能开销较大。 |
适用场景 | 对象成员是基本类型或不涉及动态分配内存时。 | 对象包含动态分配的资源(如指针、动态数组等)时。 |
效率 | 更高效,因为只复制数据本身,操作简单。 | 较低效,因为需要分配新的内存并复制数据。 |
std::unique_ptr
、std::shared_ptr
)来管理内存,避免手动管理内存的复杂性和风险。静态成员可以分为静态成员变量和静态成员函数,具体如下:
静态成员变量
静态成员函数
this
指针#include
#include
using namespace std;
// 声明类
class Student
{
public:
static int val_1;
private:
static int val_2;
};
// 全局声明
int Student::val_1 = 100;
int Student::val_2 = 200;
int main()
{
// 静态成员的访问方式
// 1、通过对象访问
Student S_1;
Student S_2;
S_1.val_1 = 110;
cout << "S_1 val_1 = " << S_1.val_1 << endl;
S_2.val_1 = 120;
cout << "S_1 val_1 = " << S_1.val_1 << endl;
cout << "S_2 val_1 = " << S_2.val_1 << endl;
// 2、通过类名访问
cout << "val_1 = " << Student::val_1 << endl;
// cout << "val_2 = " << Student::val_2 << endl;
return 0;
}
#include
#include
using namespace std;
// 声明类
class Student
{
public:
static int val_1;
private:
static int val_2;
public:
int val_3;
static void func(int val )
{
val_1 = val; // 可以使用的
// val_3 = val; // 不可以使用
cout << "调用 func " << " val_1 = " << val_1 << endl;
}
};
// 全局声明
int Student::val_1 = 100;
int Student::val_2 = 200;
int main()
{
// 静态成员的访问方式
// 1、通过对象访问
Student S_1;
S_1.func(80);
// 2、通过类名访问
Student::func(90);
return 0;
}
在C++中,匿名对象是指那些没有显式命名的对象,通常用于临时性操作或在函数调用时不需要保留的对象。它们在程序运行过程中短暂地存在,用于表达式的计算或者函数的参数传递。匿名对象的典型特征是生命周期极短,通常在它们的作用范围结束时会被销毁,不需要程序员显式地管理它们的内存。
匿名对象的特点
#include
#include
using namespace std;
class Student {
public:
string name;
int age;
Student(string n, int a) : name(n), age(a) {}
void show() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
void func(Student s) {
s.show();
}
int main() {
// 匿名对象可能的使用场景:
// 1. 初始化类数组的时候
Student hqyj[3] = {Student("小明", 18), Student("小红", 15), Student("小李", 25)};
hqyj[0].show();
hqyj[1].show();
hqyj[2].show();
// 2. 给函数传参时
func(Student("张飞", 40));
return 0;
}