一、
class MyClass {
public:
MyClass() { std::cout << "Constructor\n"; }
MyClass(const MyClass&) { std::cout << "Copy Constructor\n"; }
MyClass(MyClass&&) noexcept { std::cout << "Move Constructor\n"; }
~MyClass() { std::cout << "Destructor\n"; }
};
MyClass createMyClass() {
MyClass obj;
return obj; // 这里可能会应用RVO
}
int main() {
MyClass myObj = createMyClass();
// 如果没有RVO,这里可能会看到Copy Constructor或Move Constructor的调用
// 但由于RVO,通常只会看到Constructor和Destructor的调用
return 0;
}
结果
Constructor
Destructor
返回值优化(RVO, Return Value Optimization)
C++标准允许编译器优化函数返回对象的方式,以减少不必要的拷贝或移动操作。这被称为返回值优化(RVO)或(在某些情况下)命名返回值优化(NRVO)。当函数返回一个局部对象时,如果编译器确定这样做是安全的,它可能会直接在调用者的上下文中构造该对象,而不是在函数内部构造后再拷贝或移动到调用者。
对象的生命周期
当函数返回一个对象时,对象的生命周期从函数内部开始,但如果RVO被应用,那么从实际效果上看,对象的生命周期可能开始于调用者的上下文。然而,从语义上讲,对象的生命周期始于它在函数内部被创建时,结束于它离开作用域或被销毁时(无论这是发生在函数内部还是通过返回给调用者)。
注意事项
- 资源管理:如果对象管理资源(如动态分配的内存、文件句柄等),确保这些资源在对象生命周期结束时被正确释放。
- 移动语义:在C++11及更高版本中,通过移动语义(使用右值引用和
std::move
)可以减少不必要的拷贝,提高效率。 - 返回局部对象的引用:避免返回局部对象的引用或指针,因为当函数返回时,局部对象会被销毁,返回的引用或指针将变为悬空。
结论
在C++中,函数返回对象时,对象的生命周期从它在函数内部被创建时开始。然而,通过RVO等优化技术,可以减少或消除不必要的拷贝或移动操作,从而提高性能。重要的是要理解这些机制,并在编写代码时考虑到它们,以确保代码的正确性和效率。
二、
MyClass& createMyClass() {
MyClass obj;
return obj; // 这里可能会应用RVO
}
int main() {
MyClass myObj = createMyClass();
std::cout << "end main\n";
return 0;
}
Constructor
Destructor
Copy Constructor
end main
Destructor
如果是引用的话为什么先析构在拷贝呢
MyClass createMyClass(bool cond) {
MyClass obj1;
MyClass obj2;
if (cond) {
return obj1; // 返回路径 1
} else {
return obj2; // 返回路径 2
}
}
int main() {
MyClass myObj = createMyClass(true);
std::cout << "end main\n";
return 0;
}
Constructor
Constructor
Move Constructor
Destructor
Destructor
end main
Destructor
MyClass& createMyClass(bool cond) {
MyClass obj1;
MyClass obj2;
if (cond) {
return obj1; // 返回路径 1
} else {
return obj2; // 返回路径 2
}
}
int main() {
MyClass myObj = createMyClass(true);
std::cout << "end main\n";
return 0;
}
Constructor
Constructor
Destructor
Destructor
Copy Constructor
end main
Destructor
三、
https://blog.csdn.net/qq_43287763/article/details/136578993
NRVO最好的使用场景是一个函数只有一个返回路径,或者虽然有多个返回路径,但每个路径返回的是同一个局部命名对象。当函数中有多个局部对象,并且根据不同的条件返回不同的对象时,编译器很难进行这种优化,因为编译器必须在函数执行时才能确定返回哪个对象。这就需要在运行时进行选择,而这个选择过程无法通过在编译时就将对象直接构造在目标位置来优化。
自C++17起,标准要求编译器在可能的情况下执行RVO。但对于NRVO,即使在C++17及以后,如果存在多个返回路径,标准并不保证编译器一定能够应用NRVO。因此,当编写可能返回多个局部对象的函数时,仍然需要注意潜在的性能影响。
RVO和NRVO不仅减少了不必要的复制,还可能减少了内存分配和释放操作,这对于性能至关重要。
MyClass createMyClass(bool cond) {
MyClass obj1;
if (cond) {
MyClass obj2;
// ... 对 obj2 进行操作
return obj2; // 条件返回路径
}
// ... 对 obj1 进行操作
return obj1; // 默认返回路径
}
int main() {
MyClass myObj = createMyClass(true);
std::cout << "end main\n";
return 0;
}
结果
Constructor
Constructor
Move Constructor
Destructor
Destructor
end main
Destructor
MyClass createMyClass(bool cond) {
MyClass obj1;
if (cond) {
MyClass obj2;
// ... 对 obj2 进行操作
return obj2; // 条件返回路径
}
// ... 对 obj1 进行操作
return obj1; // 默认返回路径
}
int main() {
MyClass myObj = createMyClass(false);
std::cout << "end main\n";
return 0;
}
结果
Constructor
Move Constructor
Destructor
end main
Destructor
const int& add_return_reference(const int a, const int b) {
int sum = a + b;
return sum; // 报错reference to local variable ‘sum’ returned [-Wreturn-local-addr]
}
int main() {
int a = 10;
int b = 10;
int c = 10;
c = add_return_reference(a, b);
return 0;
}
编译失败
class B {
public:
B(){
std::cout << "B的构造函数" << std::endl;
}
B(int i){
std::cout << "带int型参数的B的构造函数" << std::endl;
}
B(const B &ano){
std::cout << "B的复制构造函数" << std::endl;
}
B(const B &&) noexcept{
std::cout << "B的移动构造函数" << std::endl;
}
B& operator=(const B& rhs){
std::cout << "B的赋值操作符" << std::endl;
return *this;
}
virtual ~B(){
std::cout << "B的析构函数" << std::endl;
}
};
B func2()
{
if(true) {
B b(10);
return b;
}
}
int main() {
B t ;
t = func2();
std::cout << "end main" << std::endl;
return 0;
}
B的构造函数
带int型参数的B的构造函数
B的移动构造函数
B的析构函数
B的赋值操作符
B的析构函数
end main
B的析构函数