C++ 最佳实践:返回引用与对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2497541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C++ best practice: Returning reference vs. object
提问by Mike Crowe
I'm trying to learn C++, and trying to understand returning objects. I seem to see 2 ways of doing this, and need to understand what is the best practice.
我正在尝试学习 C++,并试图理解返回的对象。我似乎看到了 2 种这样做的方法,并且需要了解什么是最佳实践。
Option 1:
选项1:
QList<Weight *> ret;
Weight *weight = new Weight(cname, "Weight");
ret.append(weight);
ret.append(c);
return &ret;
Option 2:
选项 2:
QList<Weight *> *ret = new QList();
Weight *weight = new Weight(cname, "Weight");
ret->append(weight);
ret->append(c);
return ret;
(of course, I may not understand this yet either).
(当然,我可能也不明白这一点)。
Which way is considered best-practice, and should be followed?
哪种方式被认为是最佳实践,应该遵循?
回答by Potatoswatter
Option 1is defective. When you declare an object
选项 1有缺陷。当你声明一个对象时
QList<Weight *> ret;
it only lives in the local scope. It is destroyed when the function exits. However, you can make this work with
它只存在于本地范围内。它在函数退出时被销毁。但是,您可以使用
return ret; // no "&"
Now, although ret
is destroyed, a copy is made first and passed back to the caller.
现在,虽然ret
被销毁了,但首先创建了一个副本并将其传回给调用者。
This is the generally preferred methodology. In fact, the copy-and-destroy operation (which accomplishes nothing, really) is usually elided, or optimized outand you get a fast, elegant program.
这是通常首选的方法。事实上,复制和销毁操作(实际上什么也不做)通常会被省略或优化掉,然后您会得到一个快速、优雅的程序。
Option 2works, but then you have a pointer to the heap. One way of looking at C++ is that the purpose of the language is to avoid manual memory management such as that. Sometimes you do want to manage objects on the heap, but option 1 still allows that:
选项 2有效,但是您有一个指向堆的指针。看待 C++ 的一种方式是,该语言的目的是避免诸如此类的手动内存管理。有时您确实希望管理堆上的对象,但选项 1 仍然允许:
QList<Weight *> *myList = new QList<Weight *>( getWeights() );
where getWeights
is your example function. (In this case, you may have to define a copy constructor QList::QList( QList const & )
, but like the previous example, it will probably not get called.)
getWeights
你的示例函数在哪里。(在这种情况下,您可能必须定义一个复制构造函数QList::QList( QList const & )
,但与前面的示例一样,它可能不会被调用。)
Likewise, you probably should avoid having a list of pointers. The list should store the objects directly. Try using std::list
… practice with the language features is more important than practice implementing data structures.
同样,您可能应该避免使用指针列表。该列表应直接存储对象。尝试使用std::list
……练习语言特性比练习实现数据结构更重要。
回答by missingfaktor
Use the option #1 with a slight change; instead of returning a reference to the locally created object, return its copy.
使用选项 #1 稍作改动;不是返回对本地创建的对象的引用,而是返回其副本。
i.e. return ret;
IE return ret;
Most C++ compilers perform Return value optimization (RVO)to optimize away the temporary object created to hold a function's return value.
大多数 C++ 编译器执行返回值优化 (RVO)以优化掉为保存函数返回值而创建的临时对象。
回答by Michael Aaron Safyan
In general, you should never return a reference or a pointer. Instead, return a copy of the object or return a smart pointer class which owns the object. In general, use static storage allocation unless the size varies at runtime or the lifetime of the object requires that it be allocated using dynamic storage allocation.
通常,您永远不应该返回引用或指针。相反,返回对象的副本或返回拥有该对象的智能指针类。通常,除非大小在运行时发生变化,或者对象的生命周期要求使用动态存储分配来分配,否则通常使用静态存储分配。
As has been pointed out, your example of returning by reference returns a reference to an object that no longer exists (since it has gone out of scope) and hence are invoking undefined behavior. This is the reason you should never return a reference. You should never return a raw pointer, because ownership is unclear.
正如已经指出的那样,您通过引用返回的示例返回对不再存在的对象的引用(因为它已超出范围),因此正在调用未定义的行为。这就是你永远不应该返回引用的原因。您永远不应该返回原始指针,因为所有权不清楚。
It should also be noted that returning by value is incredibly cheap due to return-value optimization (RVO), and will soon be even cheaper due to the introduction of rvalue references.
还应该注意的是,由于返回值优化(RVO),按值返回非常便宜,并且由于引入了右值引用,很快就会更便宜。
回答by raj
passing & returning references invites responsibilty.! u need to take care that when you modify some values there are no side effects. same in the case of pointers. I reccomend you to retun objects. (BUT IT VERY-MUCH DEPENDS ON WHAT EXACTLY YOU WANT TO DO
)
传递和返回参考文献邀请责任。!您需要注意,当您修改某些值时没有副作用。在指针的情况下也是如此。我建议你重新调整对象。( BUT IT VERY-MUCH DEPENDS ON WHAT EXACTLY YOU WANT TO DO
)
In ur Option 1, you return the address and Thats VERY bad as this could lead to undefined behaviour. (ret will be deallocated, but y'll access ret's address in the called function)
在您的选项 1 中,您返回地址,这非常糟糕,因为这可能会导致未定义的行为。(ret 将被释放,但您将在被调用函数中访问 ret 的地址)
so use return ret;
所以用 return ret;
回答by Jive Dadson
It's generally bad practice to allocate memory that has to be freed elsewhere. That's one of the reasons we have C++ rather than just C. (But savvy programmers were writing object-oriented code in C long before the Age of Stroustrup.) Well-constructed objects have quick copy and assignment operators (sometimes using reference-counting), and they automatically free up the memory that they "own" when they are freed and their DTOR automatically is called. So you can toss them around cheerfully, rather than using pointers to them.
分配必须在其他地方释放的内存通常是不好的做法。这就是我们使用 C++ 而不仅仅是 C 的原因之一。(但精明的程序员早在 Stroustrup 时代之前就用 C 编写面向对象的代码。)构造良好的对象具有快速复制和赋值运算符(有时使用引用计数) ,当它们被释放并且它们的 DTOR 被自动调用时,它们会自动释放它们“拥有”的内存。所以你可以愉快地把它们扔来扔去,而不是使用指向它们的指针。
Therefore, depending on what you want to do, the best practice is very likely "none of the above." Whenever you are tempted to use "new" anywhere other than in a CTOR, think about it. Probably you don't want to use "new" at all. If you do, the resulting pointer should probably be wrapped in some kind of smart pointer. You can go for weeks and months without ever calling "new", because the "new" and "delete" are taken care of in standard classes or class templates like std::list and std::vector.
因此,根据您想要做什么,最佳实践很可能是“以上都不是”。每当您想在 CTOR 之外的任何地方使用“new”时,请考虑一下。可能您根本不想使用“new”。如果这样做,结果指针可能应该包含在某种智能指针中。您可以连续数周甚至数月不调用“new”,因为“new”和“delete”是在标准类或类模板(如 std::list 和 std::vector)中处理的。
One exception is when you are using an old fashion library like OpenCV that sometimes requires that you create a new object, and hand off a pointer to it to the system, which takes ownership.
一个例外是当您使用像 OpenCV 这样的旧式库时,有时需要您创建一个新对象,并将指向它的指针传递给系统,系统会获得所有权。
If QList and Weight are properly written to clean up after themselves in their DTORS, what you want is,
如果 QList 和 Weight 在它们的 DTORS 中正确写入以进行清理,那么您想要的是,
QList<Weight> ret();
Weight weight(cname, "Weight");
ret.append(weight);
ret.append(c);
return ret;
回答by Agnel Kurian
As already mentioned, it's better to avoid allocating memory which must be deallocated elsewhere. This is what I prefer doing (...these days):
如前所述,最好避免分配必须在其他地方解除分配的内存。这就是我更喜欢做的(...这些天):
void someFunc(QList<Weight *>& list){
// ... other code
Weight *weight = new Weight(cname, "Weight");
list.append(weight);
list.append(c);
}
// ... later ...
QList<Weight *> list;
someFunc(list)
Even better -- avoid new
completely and using std::vector
:
更好 -new
完全避免并使用std::vector
:
void someFunc(std::vector<Weight>& list){
// ... other code
Weight weight(cname, "Weight");
list.push_back(weight);
list.push_back(c);
}
// ... later ...
std::vector<Weight> list;
someFunc(list);
You can always use a bool
or enum
if you want to return a status flag.
如果要返回状态标志,您始终可以使用bool
or enum
。
回答by user3086814
Based on experience, do not use plain pointers because you can easily forget to add proper destruction mechanisms.
根据经验,不要使用普通指针,因为您很容易忘记添加适当的销毁机制。
If you want to avoid copying, you can go for implementing the Weightclass with copy constructor and copy operator disabled:
如果您想避免复制,您可以在禁用复制构造函数和复制运算符的情况下实现Weight类:
class Weight {
protected:
std::string name;
std::string desc;
public:
Weight (std::string n, std::string d)
: name(n), desc(d) {
std::cout << "W c-tor\n";
}
~Weight (void) {
std::cout << "W d-tor\n";
}
// disable them to prevent copying
// and generate error when compiling
Weight(const Weight&);
void operator=(const Weight&);
};
Then, for the class implementing the container, use shared_ptr
or unique_ptr
to implement the data member:
然后,对于实现容器的类,使用shared_ptr
或unique_ptr
实现数据成员:
template <typename T>
class QList {
protected:
std::vector<std::shared_ptr<T>> v;
public:
QList (void) {
std::cout << "Q c-tor\n";
}
~QList (void) {
std::cout << "Q d-tor\n";
}
// disable them to prevent copying
QList(const QList&);
void operator=(const QList&);
void append(T& t) {
v.push_back(std::shared_ptr<T>(&t));
}
};
Your function for adding an element would make use or Return Value Optimization and would not call the copy constructor (which is not defined):
您添加元素的函数将使用或返回值优化,并且不会调用复制构造函数(未定义):
QList<Weight> create (void) {
QList<Weight> ret;
Weight& weight = *(new Weight("cname", "Weight"));
ret.append(weight);
return ret;
}
On adding an element, the let the container take the ownership of the object, so do not deallocate it:
添加元素时,让容器获得对象的所有权,因此不要释放它:
QList<Weight> ql = create();
ql.append(*(new Weight("aname", "Height")));
// this generates segmentation fault because
// the object would be deallocated twice
Weight w("aname", "Height");
ql.append(w);
Or, better, force the user to pass your QList implementation only smart pointers:
或者,更好地,强制用户只传递你的 QList 实现智能指针:
void append(std::shared_ptr<T> t) {
v.push_back(t);
}
And outside class QList you'll use it like:
在类 QList 之外,您将像这样使用它:
Weight * pw = new Weight("aname", "Height");
ql.append(std::shared_ptr<Weight>(pw));
Using shared_ptr you could also 'take' objects from collection, make copies, remove from collection but use locally - behind the scenes it would be only the same only object.
使用 shared_ptr 您还可以从集合中“获取”对象,制作副本,从集合中删除但在本地使用 - 在幕后,它只会是相同的唯一对象。
回答by Cross
All of these are valid answers, avoid Pointers, use copy constructors, etc. Unless you need to create a program that needs good performance, in my experience most of the performance related problems are with the copy constructors, and the overhead caused by them. (And smart pointers are not any better on this field, I'd to remove all my boost code and do the manual delete because it was taking too much milliseconds to do its job).
所有这些都是有效的答案,避免使用指针,使用复制构造函数等。除非您需要创建一个需要良好性能的程序,否则根据我的经验,大多数与性能相关的问题都与复制构造函数有关,以及由它们引起的开销。(智能指针在这个领域也没有什么好,我会删除我所有的 boost 代码并进行手动删除,因为它花费了太多毫秒来完成它的工作)。
If you're creating a "simple" program (although "simple" means you should go with java or C#) then use copy constructors, avoid pointers and use smart pointers to deallocate the used memory, if you're creating a complex programs or you need a good performance, use pointers all over the place, and avoid copy constructors (if possible), just create your set of rules to delete pointers and use valgrind to detect memory leaks,
如果您正在创建一个“简单”程序(尽管“简单”意味着您应该使用 java 或 C#)然后使用复制构造函数,避免使用指针并使用智能指针来释放使用的内存,如果您正在创建一个复杂的程序或您需要良好的性能,到处使用指针,并避免使用复制构造函数(如果可能),只需创建一组规则来删除指针并使用 valgrind 来检测内存泄漏,
Maybe I will get some negative points, but I think you'll need to get the full picture to take your design choices.
也许我会得到一些消极的观点,但我认为你需要全面了解你的设计选择。
I think that saying "if you're returning pointers your design is wrong" is little misleading. The output parameters tends to be confusing because it's not a natural choice for "returning" results.
我认为说“如果你返回指针,你的设计是错误的”是没有误导性的。输出参数往往令人困惑,因为它不是“返回”结果的自然选择。
I know this question is old, but I don't see any other argument pointing out the performance overhead of that design choices.
我知道这个问题很老,但我没有看到任何其他论点指出该设计选择的性能开销。