C++;最佳实践:返回引用与对象 我试图学习C++,试图理解返回的对象。我似乎看到了两种方法,需要了解什么是最佳实践
备选案文1:C++;最佳实践:返回引用与对象 我试图学习C++,试图理解返回的对象。我似乎看到了两种方法,需要了解什么是最佳实践,c++,C++,备选案文1: QList<Weight *> ret; Weight *weight = new Weight(cname, "Weight"); ret.append(weight); ret.append(c); return &ret; QList-ret; 重量*重量=新重量(cname,“重量”); 重新添加(重量); ret.append(c); 返回和返回; 备选案文2: QList<Weight *> *ret = new QList(); W
QList<Weight *> ret;
Weight *weight = new Weight(cname, "Weight");
ret.append(weight);
ret.append(c);
return &ret;
QList-ret;
重量*重量=新重量(cname,“重量”);
重新添加(重量);
ret.append(c);
返回和返回;
备选案文2:
QList<Weight *> *ret = new QList();
Weight *weight = new Weight(cname, "Weight");
ret->append(weight);
ret->append(c);
return ret;
QList*ret=newqlist();
重量*重量=新重量(cname,“重量”);
ret->append(重量);
ret->append(c);
返回ret;
(当然,我可能也还不明白这一点)
哪种方法被认为是最佳实践,应该遵循?使用选项1,稍作更改;返回其副本,而不是返回对本地创建对象的引用
i、 e.返回ret代码>
大多数C++编译器执行优化以创建一个函数返回值的临时对象。
< P> <强>选项1 < /StR>是有缺陷的。当您声明一个对象时
QList<Weight *> ret;
现在,尽管ret
被销毁,但首先会创建一个副本并将其传递回调用方
这是通常首选的方法。事实上,复制和销毁操作(实际上什么也做不到)通常是一个快速、优雅的程序
选项2起作用,但是您有一个指向堆的指针。一种看待C++的方法是,语言的目的是避免像这样的手动内存管理。有时您确实希望管理堆上的对象,但选项1仍然允许:
QList<Weight *> *myList = new QList<Weight *>( getWeights() );
QList*myList=newqlist(getWeights());
其中getWeights
是示例函数。(在这种情况下,您可能必须定义一个复制构造函数QList::QList(QList const&)
,但与前面的示例一样,它可能不会被调用。)
同样,您可能应该避免使用指针列表。列表应直接存储对象。尝试使用std::list
…练习语言功能比练习实现数据结构更重要。一般来说,您不应该返回引用或指针。相反,返回对象的副本或返回拥有该对象的智能指针类。通常,使用静态存储分配,除非运行时大小发生变化,或者对象的生存期要求使用动态存储分配进行分配
正如已经指出的,通过引用返回的示例返回对不再存在的对象的引用(因为它已超出范围),因此正在调用未定义的行为。这就是您不应该返回引用的原因。您永远不应该返回原始指针,因为所有权不明确
还应注意的是,由于返回值优化(RVO),按值返回非常便宜,并且由于引入了右值引用,很快将更加便宜。传递和返回引用需要负责任。!你需要注意,当你修改一些值时,没有副作用。指针的情况也是如此。我建议你重新整理物品。(但这在很大程度上取决于你到底想做什么
)
在您的选项1中,您返回地址,这非常糟糕,因为这可能导致未定义的行为。(ret将被解除分配,但y将在被调用函数中访问ret的地址)
所以使用返回ret代码>分配必须在别处释放的内存通常是不好的做法。这是我们有C++而不是C的原因之一(但是精明的程序员在StruouStrup之前很久就在C中编写面向对象代码)。构造好的对象有快速复制和赋值操作符(有时使用引用计数),它们自动释放它们自己拥有的内存。当它们被释放并自动调用其DTOR时。所以你可以愉快地把它们扔来扔去,而不是用指针指向它们
因此,根据您想要做的事情,最佳实践很可能是“以上任何一项都没有”。每当您想在CTOR以外的任何地方使用“new”时,请考虑一下。也许你根本不想用“新”这个词。如果您这样做了,那么结果指针可能应该包装在某种智能指针中。您可以连续数周或数月不调用“new”,因为“new”和“delete”在标准类或类模板(如std::list和std::vector)中处理
一个例外是,当您使用像OpenCV这样的旧式库时,有时需要您创建一个新对象,并将指向它的指针交给系统,系统将获得所有权
如果QList和Weight在其DTOR中正确写入以清理它们,那么您需要的是
QList<Weight> ret();
Weight weight(cname, "Weight");
ret.append(weight);
ret.append(c);
return ret;
QList-ret();
重量(cname,“重量”);
重新添加(重量);
ret.append(c);
返回ret;
如前所述,最好避免分配必须在别处释放的内存。这就是我喜欢做的事(…这些天):
如果要返回状态标志,您可以始终使用bool
或enum
。所有这些都是有效答案,避免指针,使用复制构造函数等。除非您需要创建需要良好性能的程序,以我的经验,大多数与性能相关的问题都与复制构造函数有关,以及由此产生的开销。(在这个字段上,智能指针并没有任何改进,我想删除所有的boost代码并执行手动删除,因为它花费了太多毫秒来完成它的工作)
如果你正在创建一个“简单”的程序(虽然“简单”意味着你应该使用java或C#),那么就使用复制构造函数,避免使用指针,并使用智能指针来释放使用过的内存,如果你正在创建一个复杂的程序或者你需要一个好的性能,则在所有地方使用指针,并避免复制构造函数(如果可能的话),只需创建一组规则来删除指针并使用val
QList<Weight> ret();
Weight weight(cname, "Weight");
ret.append(weight);
ret.append(c);
return ret;
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)
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);
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&);
};
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));
}
};
QList<Weight> create (void) {
QList<Weight> ret;
Weight& weight = *(new Weight("cname", "Weight"));
ret.append(weight);
return ret;
}
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);
void append(std::shared_ptr<T> t) {
v.push_back(t);
}
Weight * pw = new Weight("aname", "Height");
ql.append(std::shared_ptr<Weight>(pw));