确保释放向量的 C++ 技巧,但 GNU g++ 的编译问题

Posted

技术标签:

【中文标题】确保释放向量的 C++ 技巧,但 GNU g++ 的编译问题【英文标题】:C++ trick to ensure deallocation of a vector, but compilation problems with GNU g++ 【发布时间】:2011-11-03 12:48:20 【问题描述】:

以下代码演示了确保向量完全解除分配的技巧:

#include <vector>

using namespace std;

template<typename T>
class tvector : public vector<T>

    public:
    typedef vector<T> base;
    void swap(tvector<T>& v) 
        // do some other stuff, but omitted here.
        base::swap(v); 
;

int main()

    tvector<int> tv1;
    // imagine filling tv1 with loads of stuff, use it for something...

    // now by swapping with a temporary declaration of an empty tvector that should
    // go out of scope at the end of the line, we get all memory used by tv1 returned
    // to the heap
    tv1.swap(tvector<int>());

嗯,这可以使用 Visual C++ (cl.exe),但使用 GNU g++ 无法编译,出现以下错误:

test.cpp: In function ‘int main()’:
test.cpp:18:28: error: no matching function for call to ‘tvector<int>::swap(tvector<int>)’
test.cpp:10:7: note: candidate is: void tvector<T>::swap(tvector<T>&) [with T = int]

这是 g++ 中的错误,还是我的代码确实是错误的 C++ 代码?

我使用 g++ 的这种释放技巧的解决方法是:

int main()

    tvector<int> tv1;
    
        tvector<int> t;
        tv1.swap(t);
    

对此有何看法?

【问题讨论】:

我的观点是,既然它即将超出范围,你应该什么都不做,让析构函数清理它。 没有。不要从标准容器继承。不要冒险。 这对传统的swap 技巧有何改进?另请注意,感谢shrink_to_fit,C++11 使这个问题变得不那么重要了。 @KerrekSB: shrink_to_fit 没有任何保证。由实现决定是否应该进行释放。真正的问题是“std::vector&lt;int&gt;().swap(v);v = std::vector&lt;int&gt;() 相比有何改进”。 【参考方案1】:

这是众所周知的。释放向量内容的标准教科书技巧是:

std::vector<int> v;
// Do stuff with v

std::vector<int>().swap(v); // clears v

请注意,反向不起作用:

v.swap(std::vector<int>()); // compile time error

因为您试图将临时对象绑定到非 const 引用,这是被禁止的。

Visual Studio 允许将此作为​​非标准扩展,但将警告级别提升到 /W3 (IIRC) 会触发“使用非标准扩展”警告。

在 C++11 中(在技术上也是在 C++03 中!),你可以简单地做

v = std::vector<int>();

或者,如果您喜欢冗长(仅限 C++11),则有

v.clear(); // or v.resize(0);
v.shrink_to_fit(); 

但该标准并不能保证缩小请求是否会得到满足。

如果您确实需要,可以使用它,但请不要从标准容器继承。这不是一件安全的事情:您可能会调用错误的析构函数。

【讨论】:

@MagnusHoff: clear() 不会改变向量的容量,只会改变大小。没有释放内存。 太棒了。这解释了它,我只是做错了。感谢亚历山大。【参考方案2】:

您使用临时调用swap 方法,该方法不会存储在任何地方。它不能作为参考,因为从编译器的角度来看,它没有存储在任何地方。 tvector&lt;int&gt;tvector&lt;int&gt;&amp;

【讨论】:

有道理,谢谢 Joachim。一定是 Visual C++ 只是更宽容一些,并将这个临时存储在某个地方? 应该注意,虽然您不能对临时对象进行非 const 引用,但对临时对象的 const 引用是可以的。但在这种情况下,const 引用将不起作用,因为必须修改参数。但是,您可以采用临时的右值引用(在 C++11 中引入)来实现移动语义。【参考方案3】:

您正在寻找this idiom 来释放向量的保留内存:

tvector<int>().swap(tv1);

不允许将临时变量传递给swap,因为swap 采用非常量引用(换句话说,它需要一个可以修改的非临时变量)。 (我很惊讶 Visual C++ 接受这个代码。)

如果您使用Clang 而不是 gcc,它会尝试为您解释这一点:

test.cc:35:14:错误:不能对类型“tvector”的非 const 左值引用 绑定到“tvector”类型的临时 tv1.swap(tvector()); ^~~~~~~~~~~~~~ test.cc:22:27:注意:在此处将参数传递给参数“v” 无效交换(tvector& v)

正确版本的习语有效,因为它创建了一个变量,该变量为整个语句保持创建状态,然后传递一个非临时参数。

【讨论】:

以上是关于确保释放向量的 C++ 技巧,但 GNU g++ 的编译问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 GNU g++ 仅编译标准 C++?

如何在 Mac 终端中使用 C++11 支持编译 C++

编程竞赛的C++技巧

更改 GNU 编译器中的 -g 标志使 C++ 可执行 [重复]

GNU g++ 内联汇编块,如 Apple g++/Visual C++?

C++ 向量问题