共计 2255 个字符,预计需要花费 6 分钟才能阅读完成。
核心概念
基本类型赋值 vs 类对象赋值
基本类型的赋值操作非常简单,就是直接复制值:

int a = 5;
int b = a; // 基本类型赋值,直接复制值
而类对象的赋值会调用拷贝构造函数(如果定义了的话):
class MyClass {
public:
int* data;
MyClass(int size) {data = new int[size]; }
~MyClass() { delete[] data; }
// 拷贝构造函数
MyClass(const MyClass& other) {data = new int[/*size*/];
std::copy(other.data, other.data + /*size*/, data);
}
};
MyClass obj1(10);
MyClass obj2 = obj1; // 调用拷贝构造函数
函数参数传递方式
-
值传递(Pass by value):
void func(int param) {/* 操作 param */} -
引用传递(Pass by reference):
void func(int& param) {/* 操作原变量 */} -
指针传递(Pass by pointer):
void func(int* param) {/* 通过指针操作 */}
痛点分析
浅拷贝问题
class ShallowCopy {
public:
int* data;
ShallowCopy(int val) {data = new int(val); }
~ShallowCopy() { delete data;}
// 没有自定义拷贝构造函数
};
ShallowCopy a(5);
ShallowCopy b = a; // 浅拷贝
// 析构时会导致同一内存被释放两次
临时对象生命周期
void process(const std::string& str) {/*...*/}
process("temporary"); // 临时对象的生命周期只到函数结束
返回局部变量引用
int& badFunc() {
int local = 10;
return local; // 非常危险!}
解决方案
Rule of Three/Five
class SafeClass {
public:
int* data;
size_t size;
// 构造函数
SafeClass(size_t s) : size(s), data(new int[s]) {}
// 1. 析构函数
~SafeClass() { delete[] data; }
// 2. 拷贝构造函数
SafeClass(const SafeClass& other) : size(other.size), data(new int[other.size]) {std::copy(other.data, other.data + size, data);
}
// 3. 拷贝赋值运算符
SafeClass& operator=(const SafeClass& other) {if (this != &other) {delete[] data;
size = other.size;
data = new int[size];
std::copy(other.data, other.data + size, data);
}
return *this;
}
// 4. 移动构造函数(C++11)SafeClass(SafeClass&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// 5. 移动赋值运算符(C++11)SafeClass& operator=(SafeClass&& other) noexcept {if (this != &other) {delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
};
参数传递决策树
- 小对象(int, double 等)→ 值传递
- 只读大对象 → const &
- 需要修改的原对象 → &
- 可移动对象 → &&
避坑指南
explicit 关键字
class MyString {
public:
explicit MyString(int size) {/*...*/} // 阻止隐式转换
};
// MyString s = 10; // 错误,不能隐式转换
MyString s(10); // 正确,显式调用
NRVO 优化
命名返回值优化(Named Return Value Optimization)允许编译器优化掉多余的拷贝操作:
std::vector<int> createVector() {std::vector<int> result(1000); // 可能直接在调用处构造
return result; // NRVO 可能发生
}
自测题
-
以下哪种情况会导致内存问题?
class A { public: int* data; A() { data = new int(10); } ~A() { delete data;} }; A a; A b = a; -
如何修改以下函数以避免潜在问题?
std::string& getString() { std::string local = "hello"; return local; } -
哪种参数传递方式最适合大型只读对象?
答案:
1. 会导致重复释放,因为没有定义拷贝构造函数
2. 改为返回值而非引用:std::string getString()
3. const &
总结
C++ 的赋值和函数调用看似简单,但隐藏着许多陷阱。理解拷贝语义、引用和指针的区别,以及现代 C ++ 的移动语义,可以避免大多数常见问题。记住 Rule of Three/Five,合理选择参数传递方式,你的代码会安全高效很多。
正文完
