1. 构造函数的基本定义
构造函数是一种特殊的成员函数,它在对象创建时自动调用,用于初始化对象的成员变量。其名称必须与类名相同,且没有返回类型(即使是 void 也不能)。
2.构造函数的类型
2.1默认构造函数
当我们在创建类时,如果没有构建构造函数,那么编译器会自动给我们构建一个默认的构造函数。
class MyClass {
MyClass() {} // 类的构造函数
};
注意:当你使用类时 未定义构造函数 则编译器会自动实现
默认构造函数:Simple() {}(空实现)
拷贝构造函数:Simple(const Simple& other)
析构函数:~Simple() {}
2.2带参数的构造函数(自定义的构造函数)
class BankAccount {
private:
double balance; // 成员变量
string password; // 成员变量
public:
// 构造函数:类名作为函数名
BankAccount(double initBalance, string pwd)
: balance(initBalance), password(pwd) // 初始化列表
}
关键理解:构造函数是类的特殊成员函数
为什么类名能当函数名:
这是C++的语法规定:构造函数必须与类同名
编译器通过这种特殊命名识别构造函数
当创建对象时,会自动调用这个"同名函数"
2.3构造函数的工作流程
当你这样创建对象时:
BankAccount myAccount(1000.0, "secret123");
1.内存分配:为对象分配内存空间
2.调用构造函数:
myAccount.BankAccount(1000.0, "secret123");
3.执行初始化列表:
balance(initBalance) → 将参数值赋给成员变量
password(pwd) → 同上
执行构造函数体 {}(本例中为空)
2.4 初始化列表详解
: balance(initBalance), password(pwd)
| 部分 | 含义 |
| : | 初始化列表开始标志 |
| balance(initBalance) | 将参数initBalance的值赋给成员变量balance |
| , | 分隔多个初始化 |
| password(pwd) | 将参数pwd的值赋给成员变量password |
2.5为什么函数体{}可以为空?
不是必须写内容:
如果只需要初始化成员变量
不需要额外操作时,函数体可以为空
2.6 Demo
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private:
double balance; // 成员变量
string password; // 成员变量
public:
// 构造函数:类名作为函数名
BankAccount(double initBalance, string pwd)
: balance(initBalance), password(pwd) // 初始化列表
{
// 函数体:可以添加额外逻辑
cout << "账户创建成功!初始余额:" << balance << endl;
}
void showBalance(string inputPwd) {
if(inputPwd == password) {
cout << "当前余额:" << balance << endl;
} else {
cout << "密码错误!" << endl;
}
}
};
int main() {
// 创建对象:实际上是调用构造函数
BankAccount aliceAccount(500.0, "alice123");
// 使用对象
aliceAccount.showBalance("wrong"); // 输出:密码错误!
aliceAccount.showBalance("alice123"); // 输出:当前余额:500
}
具体来说:1. 在类中,如果你没有定义任何构造函数,编译器会自动生成一个默认构造函数(无参构造函数)。2. 但是一旦你定义了任何构造函数(比如你定义了一个带两个参数的构造函数),编译器就不会再自动生成默认构造函数了。
因此你可以在class中再添加一个默认构造函数,如下方实例
using namespace std;
class Student{
private:
int couts;
string password;
public:
Student() : couts(0), password("") {} // 添加默认构造函数
Student(int mun,string word):couts(mun),password(word){
cout<<"count create successful!! have"<< couts << "dllors"<<"passworld is"<< password <<"\n";
}
void showpassworld(string inputstring)
{
if(inputstring == password)
{
cout << "success" << endl;
}
else {
cout << "fail" << endl;
}
}
};
int main (int argc ,char * argv[])
{
Student account (500,"123456");
Student account1;
account.showpassworld("123456");
account.showpassworld("1234");
account1 = account;
account1.showpassworld("123456");
return 0;
}
| 概念 | 说明 | 必要性 |
| 构造函数 | ClassName(...){...} | 必须:创建对象时自动调用 |
| private | 私有成员,外部不可见 | 强烈推荐:保护数据安全 |
| public | 公共接口,外部可访问 | 必须:提供操作对象的方式 |
| 封装性 | 隐藏实现细节,暴露接口 | 面向对象核心特性 |
3.拷贝构造函数
3.1 拷贝构造函数的定义和识别
class BankAccount {
public:
// 普通构造函数
BankAccount(double initBalance, string pwd)
: balance(initBalance), password(pwd) {}
// 拷贝构造函数
BankAccount(const BankAccount& other)
: balance(other.balance), password(other.password) {}
};
关键点:
签名固定:
ClassName(const ClassName&)参数:必须是同类型的常量引用
调用时机:当使用同类型的现有对象初始化新对象时
3.2编译器根据初始化方式自动选择构造函数
| 创建方式 | 构造函数类型 | 示例 |
| Type obj; | 默认构造函数 | BankAccount acc; |
| Type obj(args); | 匹配参数的构造函数 | BankAccount acc(1000.0, "pwd"); |
| Type obj = existingObj; | 拷贝构造函数 | BankAccount acc2 = acc1 |
| Type obj(existingObj) | 拷贝构造函数 | BankAccount acc3(acc1); |
3.3默认拷贝构造函数
如果你没有写拷贝构造函数,则系统会帮你生成
class SimpleAccount {
double balance;
string password;
public:
SimpleAccount(double b, string p) : balance(b), password(p) {}
// 没有声明拷贝构造函数
};
int main() {
SimpleAccount acc1(1000.0, "pwd");
SimpleAccount acc2 = acc1; // 使用编译器生成的默认拷贝构造
}
编译器会自动生成一个浅拷贝的拷贝构造函数
// 编译器生成的等价代码
SimpleAccount(const SimpleAccount& other)
: balance(other.balance), password(other.password) {}
3.4浅拷贝与深拷贝
3.4.1浅拷贝
浅拷贝可由编译器主动生成,当我们使用时,可以不主动自定义直接在我们函数中使用;浅拷贝更方便我们使用,但是注意浅拷贝不能使用在 包含指针成员 、文件句柄/网络连接、禁止拷贝、等使用!!!!
以下为错误实例!!
class DangerousAccount {
int* securityCode; // 指针成员
public:
DangerousAccount(int code) {
securityCode = new int(code);
}
~DangerousAccount() {
delete securityCode; // 释放内存
}
};
int main() {
DangerousAccount acc1(12345);
DangerousAccount acc2 = acc1; // 浅拷贝!
// 问题1:两个对象共享同一内存
// 问题2:析构时双重释放(崩溃!)
}
此问题我们可以使用自定义拷贝构建函数或者深拷贝解决。
3.4.2 深拷贝
class SafeAccount {
int* securityCode;
public:
// 普通构造
SafeAccount(int code) : securityCode(new int(code)) {}
// 拷贝构造(深拷贝)
SafeAccount(const SafeAccount& other)
: securityCode(new int(*other.securityCode)) {}
~SafeAccount() {
delete securityCode;
}
};
使用深拷贝可解决浅拷贝会出现的问题。
3.4.3 何时需要自定义拷贝构造函数?
| 情况 | 示例 | 必要性 |
| 包含指针成员 | int* data | 必须(避免浅拷贝问题) |
| 资源管理 | 文件句柄/网络连接 | 必须 |
| 需要特殊日志 | 记录拷贝操作 | 可选 |
| 禁止拷贝 | 单例类 | 声明为delete |
3.4.3.1 default:显式使用默认拷贝
BankAccount(const BankAccount&) = default; // 显式使用默认拷贝
3.4.3.1 delete 禁止拷贝
BankAccount(const BankAccount&) = delete; // 禁止拷贝
4.析构函数
构造析构函数的使用方法有很多很多 设计到的知识点也很多 这里只简单介绍其基本用法,其余内容需要在工作中,或者学习站内大佬代码和博客进行学习理解
4.1析构函数定义
析构函数(Destructor)是一种特殊的成员函数,在对象销毁时自动调用,用于:
1.释放对象占用的资源(内存、文件句柄、网络连接等)
2.执行清理操作
3.确保资源安全释放,避免泄漏
4.2基本特性
| 特性 | 说明 |
| 命名规则 | ~类名() |
| 无参数 | 不能带任何参数 |
| 无返回值 | 不声明返回类型 |
| 自动调用 | 对象销毁时自动执行 |
| 不可重载 | 每个类只能有一个析构函数 |
4.3 默认析构函数
class MyClass {
public:
// 构造函数
MyClass() { /* 分配资源 */ }
// 析构函数
~MyClass() { /* 释放资源 */ }
};
即使你不主动构建析构函数,编译器也会自动创建。
4.4 构造析构函数
4.4.1 动态内存管理(必须使用)
当类管理动态分配的内存时,必须使用析构函数释放内存
class DynamicArray {
int* data;
size_t size;
public:
// 构造函数:分配内存
DynamicArray(size_t sz) : size(sz), data(new int[sz]) {
std::cout << "分配 " << sz << " 个整数\n";
}
// 析构函数:释放内存
~DynamicArray() {
delete[] data; // 关键:释放数组内存
std::cout << "释放 " << size << " 个整数\n";
}
// 禁止拷贝(避免浅拷贝问题)
DynamicArray(const DynamicArray&) = delete;
DynamicArray& operator=(const DynamicArray&) = delete;
};
// 使用示例
void testArray() {
DynamicArray arr(100); // 构造时分配内存
// 使用数组...
}
在函数结束后
自动调用:在对象销毁时自动执行析构函数。
4.4.2 文件资源管理(先了解)
确保文件在对象销毁时自动关闭
#include <fstream>
class LogFile {
std::ofstream file;
public:
// 构造函数:打开文件
LogFile(const std::string& filename) : file(filename) {
if (!file.is_open()) {
throw std::runtime_error("无法打开日志文件");
}
}
// 析构函数:关闭文件
~LogFile() {
if (file.is_open()) {
file.close();
std::cout << "日志文件已关闭\n";
}
}
void write(const std::string& message) {
file << message << std::endl;
}
};
// 使用示例
void logTest() {
LogFile logger("app.log"); // 打开文件
logger.write("程序启动");
logger.write("执行操作...");
} // 离开作用域时自动关闭文件
4.4.3. 数据库连接管理(先了解)
确保数据库连接在对象销毁时自动关闭
class DatabaseConnection {
// 伪代码,实际实现取决于数据库库
void* dbHandle;
public:
DatabaseConnection(const std::string& connStr) {
dbHandle = connectToDatabase(connStr);
if (!dbHandle) throw "连接失败";
}
~DatabaseConnection() {
if (dbHandle) {
disconnect(dbHandle);
std::cout << "数据库连接已关闭\n";
}
}
void executeQuery(const std::string& sql) {
// 执行SQL查询
}
};
4.4.4 锁管理(RAII模式)(先了解)
#include <mutex>
class LockGuard {
std::mutex& mtx;
public:
// 构造函数:获取锁
explicit LockGuard(std::mutex& m) : mtx(m) {
mtx.lock();
std::cout << "锁已获取\n";
}
// 析构函数:释放锁
~LockGuard() {
mtx.unlock();
std::cout << "锁已释放\n";
}
// 禁止拷贝
LockGuard(const LockGuard&) = delete;
LockGuard& operator=(const LockGuard&) = delete;
};
// 使用示例
std::mutex resourceMutex;
void safeAccess() {
LockGuard lock(resourceMutex); // 获取锁
// 安全访问共享资源...
} // 离开作用域时自动释放锁
4.5 析构函数的高级应用 (先了解)
4.5.1 虚析构函数(多态基类必备)
当类被设计为基类时,必须声明虚析构函数
class Base {
public:
virtual ~Base() { // 虚析构函数
std::cout << "Base 析构\n";
}
};
class Derived : public Base {
int* extraData;
public:
Derived() : extraData(new int[100]) {}
~Derived() override { // 覆盖基类虚析构函数
delete[] extraData;
std::cout << "Derived 析构\n";
}
};
// 使用示例
void polymorphismTest() {
Base* obj = new Derived();
// 使用对象...
delete obj; // 正确调用Derived的析构函数
}
输出
Derived 析构 Base 析构
4.5.2继承体系中的析构顺序
class Grandparent {
public:
Grandparent() { std::cout << "Grandparent 构造\n"; }
virtual ~Grandparent() { std::cout << "Grandparent 析构\n"; }
};
class Parent : public Grandparent {
public:
Parent() { std::cout << "Parent 构造\n"; }
~Parent() override { std::cout << "Parent 析构\n"; }
};
class Child : public Parent {
DynamicArray arr;
public:
Child() : arr(10) { std::cout << "Child 构造\n"; }
~Child() override { std::cout << "Child 析构\n"; }
};
// 使用示例
void inheritanceTest() {
Child child;
}
输出
Grandparent 构造
Parent 构造
分配 10 个整数
Child 构造
Child 析构
释放 10 个整数
Parent 析构
Grandparent 析构
4.6析构函数的处理流程
当对象被销毁时,编译器会按照以下顺序处理:
-
执行析构函数体:运行用户定义的析构代码
-
销毁成员变量:按声明逆序调用成员的析构函数
-
销毁基类部分:调用基类析构函数(如果有)
-
释放内存:对象占用的内存被回收

注意:每当你在构造函数中分配资源时,立即在析构函数中添加对应的释放代码。这种"分配-释放"对称性是编写安全C++代码的黄金法则。
1176

被折叠的 条评论
为什么被折叠?



