一、默认成员函数
1. 什么是默认成员函数?
在C++中,每个类都有一些特殊的成员函数,如果程序员没有显式地声明,编译器会自动为类生成这些函数,这些函数称为默认成员函数
2. 默认成员函数列表
- 默认构造函数(Default Constructor)
- 默认析构函数(Destructor)
- 默认拷贝构造函数(Copy Constructor)
- 默认拷贝赋值运算符(Copy Assignment Operator)
- 默认移动构造函数(Move Constructor,C++11引入)
- 默认移动赋值运算符(Move Assignment Operator,C++11引入)
3. 编译器何时生成默认成员函数?
- 显式声明:如果程序员没有提供某个默认成员函数的定义,编译器会根据需要自动生成
- 特别注意:一旦程序员显式地声明了任何一个拷贝或移动操作,编译器将不会为该类生成移动操作,需要手动提供
4. 代码示例
#include <iostream>
using namespace std;class Example {
public:int value;// 未显式声明任何默认成员函数
};int main() {Example ex1; // 调用默认构造函数ex1.value = 10;Example ex2 = ex1; // 调用默认拷贝构造函数Example ex3;ex3 = ex1; // 调用默认拷贝赋值运算符cout << "ex1.value = " << ex1.value << endl;cout << "ex2.value = " << ex2.value << endl;cout << "ex3.value = " << ex3.value << endl;return 0;
}
二、构造函数
1. 什么是构造函数?
构造函数(Constructor)是在创建对象时自动调用的特殊成员函数,用于初始化对象的成员变量。构造函数的名称与类名相同
2. 特点
- 没有返回类型(连
void
也没有) - 可以有参数(参数化构造函数)
- 支持函数重载,即可以有多个构造函数
- 可以在构造函数初始化列表中初始化成员变量
3. 默认构造函数
如果程序员未提供任何构造函数,编译器会为类生成一个默认构造函数,它对基本类型成员变量不进行初始化
4. 代码示例:
1.默认构造函数
#include <iostream>
using namespace std;class Person {
public:string name;int age;// 默认构造函数Person() {name = "Unknown";age = 0;}void display() const {cout << "姓名:" << name << ", 年龄:" << age << endl;}
};int main() {Person p; // 调用默认构造函数p.display();return 0;
}
2.参数化构造函数
#include <iostream>
using namespace std;class Person {
public:string name;int age;// 参数化构造函数Person(const string& n, int a) {name = n;age = a;}void display() const {cout << "姓名:" << name << ", 年龄:" << age << endl;}
};int main() {Person p("Alice", 25); // 调用参数化构造函数p.display();return 0;
}
3.构造函数初始化列表
#include <iostream>
using namespace std;class Point {
private:int x;int y;public:// 使用初始化列表初始化成员变量Point(int xCoord, int yCoord) : x(xCoord), y(yCoord) {}void display() const {cout << "坐标:(" << x << ", " << y << ")" << endl;}
};int main() {Point pt(3, 4);pt.display();return 0;
}
5. 注意事项
- 如果类中有
const
成员变量或引用类型成员,必须使用初始化列表进行初始化 - 构造函数可以被重载,允许创建多个具有不同参数列表的构造函数
三、析构函数
1. 什么是析构函数?
析构函数(Destructor)是在对象生命周期结束时自动调用的特殊成员函数,用于释放对象占用的资源(如内存、文件等)。析构函数的名称是在类名前加上~
符号
2. 特点
- 没有参数
- 没有返回类型
- 每个类只有一个析构函数,不能重载
- 编译器会自动调用析构函数,无需手动调用
3. 代码示例
#include <iostream>
using namespace std;class Resource {
public:Resource() {cout << "资源已分配。" << endl;}~Resource() {cout << "资源已释放。" << endl;}
};int main() {cout << "进入main函数。" << endl;{Resource res; // 创建对象,调用构造函数} // 离开作用域,调用析构函数cout << "退出main函数。" << endl;return 0;
}
4. 在析构函数中释放资源
当类中使用了动态内存分配(如使用new
关键字),需要在析构函数中释放内存,防止内存泄漏
#include <iostream>
using namespace std;class Array {
private:int* data;int size;public:Array(int s) : size(s) {data = new int[size];cout << "数组已分配。" << endl;}~Array() {delete[] data;cout << "数组已释放。" << endl;}
};int main() {Array arr(10);// 使用数组...return 0;
}
5. 注意事项
- 析构函数必须为公有成员函数,否则对象在离开作用域时无法正确销毁
- 避免在析构函数中抛出异常,这可能导致程序不可预测的行为
四、拷贝构造函数
1. 什么是拷贝构造函数?
拷贝构造函数(Copy Constructor)是使用同类的另一个对象来初始化新对象时调用的构造函数。它用于定义对象的拷贝行为
2. 语法
ClassName(const ClassName& other);
参数为同类对象的引用,通常为const
引用,避免不必要的拷贝
3. 默认拷贝构造函数
- 如果程序员未提供拷贝构造函数,编译器会自动生成默认的拷贝构造函数,执行浅拷贝
- 对于没有动态内存分配的类,默认的拷贝构造函数通常够用
4. 浅拷贝与深拷贝
- 浅拷贝(Shallow Copy):拷贝对象的成员变量值,对于指针成员,仅拷贝指针值,两个对象指向同一内存位置
- 深拷贝(Deep Copy):在拷贝指针成员时,为新对象分配独立的内存空间,并复制内容
5. 示例
1.默认拷贝构造函数(浅拷贝)
#include <iostream>
using namespace std;class Shallow {
public:int* data;Shallow(int val) {data = new int(val);}~Shallow() {delete data;}
};int main() {Shallow obj1(5);Shallow obj2 = obj1; // 调用默认拷贝构造函数cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 10;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}
问题:
由于是浅拷贝,obj1
和obj2
的data
指向同一内存,当一个对象被析构时,内存被释放,另一个对象再使用时会导致悬空指针
2.自定义拷贝构造函数(深拷贝)
#include <iostream>
using namespace std;class Deep {
public:int* data;Deep(int val) {data = new int(val);}// 自定义拷贝构造函数Deep(const Deep& other) {data = new int(*(other.data));}~Deep() {delete data;}
};int main() {Deep obj1(5);Deep obj2 = obj1; // 调用自定义拷贝构造函数cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 10;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}
6. 注意事项
- 拷贝构造函数的参数必须是引用,否则会导致无限递归
- 当类中有指针成员,且需要独立的内存空间,必须提供自定义的拷贝构造函数(深拷贝)
五、赋值运算符函数
1. 什么是赋值运算符函数?
赋值运算符函数(Assignment Operator Function)用于定义对象间赋值行为(operator=
)。类似于拷贝构造函数,它也需要考虑浅拷贝和深拷贝
2. 语法
ClassName& operator=(const ClassName& other);
- 返回类型为引用,返回当前对象自身
*this
,以支持链式赋值 - 参数为同类对象的**
const
引用**
3. 默认赋值运算符
- 如果程序员未提供赋值运算符函数,编译器会生成默认的赋值运算符,执行浅拷贝
4. 示例
1.默认赋值运算符(浅拷贝)
#include <iostream>
using namespace std;class Shallow {
public:int* data;Shallow(int val) {data = new int(val);}~Shallow() {delete data;}
};int main() {Shallow obj1(5);Shallow obj2(10);obj2 = obj1; // 使用默认赋值运算符cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 15;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}
2.自定义赋值运算符函数(深拷贝)
#include <iostream>
using namespace std;class Deep {
public:int* data;Deep(int val) {data = new int(val);}Deep(const Deep& other) {data = new int(*(other.data));}// 自定义赋值运算符函数Deep& operator=(const Deep& other) {if (this == &other) {return *this; // 检查自赋值}delete data; // 释放原有内存data = new int(*(other.data)); // 分配新内存并拷贝return *this;}~Deep() {delete data;}
};int main() {Deep obj1(5);Deep obj2(10);obj2 = obj1; // 使用自定义赋值运算符cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;// 修改obj1的数据*(obj1.data) = 15;cout << "修改obj1后:" << endl;cout << "obj1.data = " << *(obj1.data) << endl;cout << "obj2.data = " << *(obj2.data) << endl;return 0;
}
5. 注意事项
- 检查自赋值:在赋值运算符函数中,应检查
this
和other
是否为同一对象,避免释放自己 - 释放原有资源:在进行赋值前,应释放对象原有的资源,防止内存泄漏
- 返回
*this
的引用,支持链式赋值
六、const
成员函数
1. 什么是const
成员函数?
const
成员函数是指在函数声明后加上const
关键字的成员函数,表示该函数不会修改对象的成员变量(除非成员变量被声明为mutable
)
2. 语法
返回类型 函数名(参数列表) const;
3. 特点
const
成员函数只能调用其他const
成员函数,不能调用非const
成员函数- 可以被
const
对象调用,而非const
成员函数不能被const
对象调用
4. 代码示例
#include <iostream>
using namespace std;class Sample {
private:int value;public:Sample(int v) : value(v) {}int getValue() const {return value;}void setValue(int v) {value = v;}
};int main() {const Sample s(10); // 常量对象cout << "值是:" << s.getValue() << endl;// s.setValue(20); // 错误,不能调用非const成员函数return 0;
}
5. const
对象和成员函数
const
对象:对象被声明为const
,只能调用其const
成员函数,不能修改成员变量- 非
const
对象:可以调用所有成员函数,包括const
和非const
6. 成员函数重载
可以根据const
性对成员函数进行重载
代码示例:
#include <iostream>
using namespace std;class Example {
public:void func() {cout << "非const版本的func()" << endl;}void func() const {cout << "const版本的func()" << endl;}
};int main() {Example e;e.func(); // 调用非const版本const Example ce;ce.func(); // 调用const版本return 0;
}
七、取地址及const
取地址操作符重载
1. 取地址操作符operator&
默认情况下,对象的取地址操作会返回对象的内存地址。可以通过重载operator&
来改变取地址操作的行为
2. 为什么需要重载取地址操作符?
- 在某些情况下,我们希望隐藏对象的内部地址,或提供特殊的地址计算方式
- 可以用于智能指针的实现
3. 语法
ClassName* operator&();
const ClassName* operator&() const;
4. 示例
1.重载取地址操作符
#include <iostream>
using namespace std;class MyClass {
public:int value;MyClass(int v) : value(v) {}// 重载取地址操作符MyClass* operator&() {cout << "自定义取地址操作符被调用。" << endl;return this;}// 重载const版本const MyClass* operator&() const {cout << "自定义const取地址操作符被调用。" << endl;return this;}
};int main() {MyClass obj(10);MyClass* ptr = &obj; // 调用自定义取地址操作符cout << "value = " << ptr->value << endl;const MyClass cobj(20);const MyClass* cptr = &cobj; // 调用自定义const取地址操作符cout << "const value = " << cptr->value << endl;return 0;
}
5. 注意事项
- 小心使用:重载取地址操作符可能导致代码难以理解和维护,应该谨慎使用
- 避免陷阱:重载后,可能会影响模板代码或标准库的使用,需要确保兼容性