C++;最佳实践:返回引用与对象 我试图学习C++,试图理解返回的对象。我似乎看到了两种方法,需要了解什么是最佳实践

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

备选案文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();
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));