在C+中返回自动本地对象+; 我试图理解C++的一些方面。
我编写了这个简短的程序来展示从C++函数返回对象的不同方式:在C+中返回自动本地对象+; 我试图理解C++的一些方面。,c++,return,copy-constructor,C++,Return,Copy Constructor,我编写了这个简短的程序来展示从C++函数返回对象的不同方式: #include <iostream> using namespace std; // A simple class with only one private member. class Car{ private: int maxSpeed; public: Car( int ); void print(); Car& op
#include <iostream>
using namespace std;
// A simple class with only one private member.
class Car{
private:
int maxSpeed;
public:
Car( int );
void print();
Car& operator= (const Car &);
Car(const Car &);
};
// Constructor
Car::Car( int maxSpeed ){
this -> maxSpeed = maxSpeed;
cout << "Constructor: New Car (speed="<<maxSpeed<<") at " << this << endl;
}
// Assignment operator
Car& Car::operator= (const Car &anotherCar){
cout << "Assignment operator: copying " << &anotherCar << " into " << this << endl;
this -> maxSpeed = anotherCar.maxSpeed;
return *this;
}
// Copy constructor
Car::Car(const Car &anotherCar ) {
cout << "Copy constructor: copying " << &anotherCar << " into " << this << endl;
this->maxSpeed = anotherCar.maxSpeed;
}
// Print the car.
void Car::print(){
cout << "Print: Car (speed=" << maxSpeed << ") at " << this << endl;
}
// return automatic object (copy object on return) (STACK)
Car makeNewCarCopy(){
Car c(120);
return c; // object copied and destroyed here
}
// return reference to object (STACK)
Car& makeNewCarRef(){
Car c(60);
return c; // c destroyed here, UNSAFE!
// compiler will say: warning: reference to local variable ‘c’ returned
}
// return pointer to object (HEAP)
Car* makeNewCarPointer(){
Car * pt = new Car(30);
return pt; // object in the heap, remember to delete it later on!
}
int main(){
Car a(1),c(2);
Car *b = new Car(a);
a.print();
a = c;
a.print();
Car copyC = makeNewCarCopy(); // safe, but requires copy
copyC.print();
Car &refC = makeNewCarRef(); // UNSAFE
refC.print();
Car *ptC = makeNewCarPointer(); // safe
if (ptC!=NULL){
ptC -> print();
delete ptC;
} else {
// NULL pointer
}
}
现在,我有以下问题:
- makeNewCarCopy安全吗?是否在函数结束时复制和销毁本地对象?如果是这样,为什么不调用重载赋值操作符呢?它是否调用默认的复制构造函数 我的直觉告诉我使用<代码> MKENEWCARPOCHECUT/COMPUT>作为从C++函数/方法返回对象的最常用方式。我说得对吗
- 是的,
makeNewCarCopy
是安全的。理论上,当函数退出时会有一个副本,但因为编译器被允许删除该副本
实际上,这意味着makeNewCarCopy
将有一个隐藏的第一个参数,该参数是对未初始化的Car
的引用,而makeNewCarCopy
内部的构造函数调用将实际初始化驻留在函数堆栈框架之外的Car
实例
至于第二个问题:返回必须释放的指针不是首选方法。这是不安全的,因为函数如何分配Car
实例的实现细节被泄露了,调用方需要清理它。如果您需要动态分配,那么我建议您返回一个std::shared\u ptr
makeNewCarCopy安全吗?是否正在复制和销毁本地对象
在函数的末尾?如果是这样的话,为什么不调用重载
分配操作员?它是否调用默认的复制构造函数
这里的重要问题是“makeNewCarCopy安全吗?”该问题的答案是“是”。您正在制作对象的副本并按值返回该副本。您不会试图返回对本地自动对象的引用,这在新手中是一个常见的陷阱,这很好
这个问题的其他部分的答案就不那么重要了,尽管一旦您知道如何安全地完成这项工作,它们可能会在生产代码中变得至关重要。您可能看到也可能看不到局部对象的构造和破坏。事实上,您可能不会,尤其是在启用优化的情况下编译时。原因是编译器知道您正在创建一个临时文件并返回该文件,而该文件又被复制到其他地方。从某种意义上说,临时副本变得毫无意义,因此编译器跳过了整个烦人的create-copy-destroy步骤,直接在变量中构造新副本,这是它最终想要的。这就是所谓的。编译器可以对您的程序进行任何和所有更改,只要可观察到的行为与未进行任何更改相同(请参见:),即使复制构造函数有副作用(请参见:)
我的直觉告诉我使用makeNewCarPointer作为最常用的方法
从C++函数/方法返回对象。我说得对吗
不,考虑复制删减,正如我上面描述的那样。所有当代的主要编译器都实现了这种优化,并且做得非常好。所以,如果您可以像按指针复制一样高效地(至少)按值复制,那么按指针复制在性能方面有什么好处吗
答案是否定的。如今,你通常希望按价值回报,除非你有迫切的需要不这样做。在这些引人注目的需求中,当您需要返回的对象超过创建它的“范围”时——但性能不在其中。事实上,动态分配在时间上比自动(即“堆栈”)分配要昂贵得多。- 是
是安全的。在大多数情况下,它是有效的,因为编译器可以进行某些优化,例如(您没有看到调用赋值运算符或复制构造函数的原因)和/或C++11添加的优化makeNewCarCopy
可能非常有效,但同时也非常危险。问题是您很容易忽略返回值,编译器不会产生任何警告。因此,至少应该返回智能指针,如makeNewCarPointer
或std::unique\u ptr
。但以前的方法更可取,至少不会慢。如果由于不同的原因必须在堆上创建对象,情况就不同了std::shared\u ptr
std::unique_ptr
通常比std::shared_ptr
@Yakk更好。我发现unique_ptr
具有不必要的限制,它唯一更好的地方就是性能。我从来没有想过“我真希望我在这里使用了一个独特的ptr
!”。我只在它从未离开类/模块时才使用unique\u ptr
,这里不是这种情况。shared\u ptr
的问题是,它的生存期和所有权现在不清楚。在几乎所有情况下,一个对象都应该由另一个数据结构所拥有:当一个对象由多个数据结构所拥有时,您往往会得到相当于意大利面条代码的资源管理shared_ptr
表示对象生命周期的共享管理和所有权,这是一种特殊情况,不应该是默认情况。是的,它让你做的事情远不止是独一无二的,但其中很多都是坏习惯@雅克我想我们同意,但我们对目前的情况有误解。我考虑<代码> MKENEWWCARIOS一个公共库函数,它的任务是创建代码< CAR> > <代码> s。使用unique_ptr
或shared_ptr
与所有权、架构、接口契约以及持有它的对象无关。是的,您可以拒绝使用shared_ptr的任何功能
Constructor: New Car (speed=1) at 0x7fff51be7a38
Constructor: New Car (speed=2) at 0x7fff51be7a30
Copy constructor: copying 0x7fff51be7a38 into 0x7ff60b4000e0
Print: Car (speed=1) at 0x7fff51be7a38
Assignment operator: copying 0x7fff51be7a30 into 0x7fff51be7a38
Print: Car (speed=2) at 0x7fff51be7a38
Constructor: New Car (speed=120) at 0x7fff51be7a20
Print: Car (speed=120) at 0x7fff51be7a20
Constructor: New Car (speed=60) at 0x7fff51be79c8
Print: Car (speed=60) at 0x7fff51be79c8
Constructor: New Car (speed=30) at 0x7ff60b403a60
Print: Car (speed=30) at 0x7ff60b403a60