C++:向量实现和动态内存分配

3
我希望了解C++中如何实现vector。以前有一个相关的问题(链接),我看了一下,有一个小问题。假设链接提问中的实现是正确的,请看这段代码:
int main(){
    Vector<int> test2 = test_Vector();
    cout << test2[0] << endl;
    return 0;
}


// below is NOT the STL vector object, but the one in the linked question,
// in which the asker tries to implement STL vector himself/herself 
Vector<int> test_Vector(){
    Vector<int> test;
    test.push_back(5);
    return test;
}

据我理解,test Vector 对象是在本地创建的,所以当 test_Vector 方法返回时,局部对象超出范围,从而调用析构函数并 delete 动态数组。既然代码实际上可以运行并打印出 5,那我猜我的理解是错误的。正确的解释是什么?

2
请参阅:返回值优化(RVO) - Andrew Tomazos
RVO(返回值优化)的重要性非常高:经常有新接触C++的程序员因为担心昂贵的复制而找到了不返回对象的复杂方法。但实际上往往根本没有复制。 - juanchopanza
3个回答

3
你是正确的,但你忽略了一个重要的事情。
因为你返回了一个Vector<int>,所以你应该把它想象成被复制了。这通常会调用复制构造函数,将test复制到一个新的Vector<int>实例中。复制构造函数在链接的问题中实现为:
template<class T>
Vector<T>::Vector(const Vector<T> & v)
{
    my_size = v.my_size;
    my_capacity = v.my_capacity;
    buffer = new T[my_size];  
    for (int i = 0; i < my_size; i++)
        buffer[i] = v.buffer[i];  
}

注意,由于返回值优化(请参见下面的注释中的细节),可能不会调用复制构造函数。在许多情况下,编译器可以优化复制过程,而C ++标准允许这种优化可能会改变程序行为
无论是复制对象还是应用RVO,最终结果都应该相同。只要遵循正常的面向对象实践,优化就不应破坏您的对象。
无论类型如何,您都应该始终将函数返回值视为按值传递(即复制),然后考虑您的编译器可能正在执行RVO。重要的是不要忘记三原则(或四原则、五原则)。

1
当然,编译器可以通过RVO优化临时对象,因此如果您在复制构造函数中有逻辑,则可能会被跳过。 :) - Joe
1
请将“它将调用复制构造函数”更改为“它可能会调用复制构造函数”。 - Slava
1
没错,但我认为当涉及到指针时,编译器很难做到这一点并确保它正在执行正确的操作。这就是为什么在C++11中引入了移动语义的原因之一。 - paddy
1
@paddy 请阅读关于 RVO 的内容,你关于 inline 的评论也不相关。 - Slava
按照我的理解,标准允许RVO但不要求它。 - paddy
显示剩余7条评论

1
他提供了一个公共的拷贝构造函数(技术上,他没有将拷贝构造函数设为私有),因此标准的C++逻辑会启动并复制对象返回。新对象是局部于main的。在拷贝构造函数内部,分配了新的内存,并将数据复制过去。如果他没有提供拷贝构造函数,它将访问无效的内存(试图访问已释放的指针所占用的内存)。

1
在现实生活中,通过返回值优化省略复制是非常可能的。 - juanchopanza
1
@juanchopanza 确实,但这不是你应该考虑的方式,因为那是一种优化而不是特性,如果复制构造函数存在问题,你会得到根据优化而异的行为。 - Dave
1
当然,但是硬币的另一面是你也必须预期可能没有调用复制构造函数(在上述情况中,肯定不会调用),因为这是一种允许改变程序可观察行为的优化。 - juanchopanza

1
test 被返回时,它的拷贝构造函数会被调用(理论上),该函数会分配一块新的内存并复制 test 的内容。原本在 test_Vector 栈上的 test 会被销毁(理论上),而 test2 则获得了这个拷贝,这意味着赋值运算符会被调用(理论上),它会再次分配内存并从创建临时对象时复制数据。从 test_Vector 返回的临时对象随后会被销毁(理论上)。
如您所见,如果没有编译器优化,这将是一场噩梦 :) 如果您不做太过复杂的事情,编译器很聪明,而且您的情况很简单,则可以跳过 "理论上" 这些字眼。请参阅 this 以获取有关 C++11 的更新信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接