【问题标题】:Vector of object pointers, initialisation对象指针向量,初始化
【发布时间】:2011-05-09 18:14:17
【问题描述】:

我对 C++ 还不是很有经验,所以如果这是基本的东西,请耐心等待。

我有一些类似下面的代码。 L 是一个抽象类(它有许多纯虚函数),而ABC 都是从L 派生的常规类。这些可能有任意数量,而且它们都是不同的。

int main() {
    // ...

    std::vector<L*> ls(3) ; 

    ls[0] = new A ;
    ls[1] = new B ;
    ls[2] = new C ;

    int i ;
    for (i = 0 ; i < ls.size() ; i++) {
        if (ls[i]->h()) {
            // ...
        }
    }

    // ...
}

它有效,但确实必须有更好的方法来初始化该向量。对吧?

向量在第一次初始化后不应该改变。但是,我认为我不能将其设为 const,因为各种对象本身可能会在内部发生变化。我在常规数组上选择了一个向量,因为我不想手动跟踪它的长度(这被证明容易出错)。

理想情况下,我想将向量的定义和初始化从main 中提取出来,最好放到一个单独的文件中,然后我可以在#include 中提取。当我尝试编译器抱怨它“在‘=’标记之前需要构造函数、析构函数或类型转换”时。所有ABC 类都有默认构造函数。

另外,我的印象是我必须手动 delete 使用 new 创建的任何内容,但它不会使用 deletedelete[] 删除 ls。如果我尝试delete ls;,编译器会抱怨“type ‘class std::vector >’ 参数被赋予‘delete’,预期指针”。

以上是安全的还是会导致一些内存问题?

【问题讨论】:

  • A、B、C的析构函数到底是做什么的?
  • 我没有给他们一个析构函数,所以我想不管编译器默认提供什么。我应该给他们自定义析构函数吗?
  • 关键是,如果析构函数没有做任何特殊的事情(比如写入日志文件或其他东西),你可能根本不用在指针上调用delete,因为内存无论如何,程序退出时都会被回收。
  • 我明白了。但是,我认为为了安全起见删除它们并没有什么坏处?特别是如果我以后要向其中一个或全部添加一个析构函数。
  • 不,调用析构函数绝对没有坏处。我只是怀疑跳过确保调用析构函数所必需的箍是否值得额外的复杂性。请注意,简单地将一堆delete 语句放在main 函数的底部并不能保证将调用析构函数,因为如果之前抛出异常,控制流可能永远不会到达这些语句。跨度>

标签: c++ stdvector


【解决方案1】:

但确实必须有更好的方法来初始化该向量。对吧?

我不这么认为,至少在没有 C++0x 的情况下不会。你更喜欢哪种方式?你的初始化代码完全没问题。

但我认为我不能将其设为 const,因为各种对象本身可能会在内部发生变化。

你仍然可以将向量本身设为const,只是它的成员类型不能是指向const的指针。

我在常规数组上选择了一个向量,因为我不想手动跟踪它的长度(这被证明容易出错)。

您不必跟踪常量数组中的长度:

L* ls[] = { new A, new B, new C };
// with <boost/range/size.hpp>
std::size_t num = boost::size(ls);
// without Boost, more error-prone
// std::size_t num = sizeof ls / sizeof ls[0];

而且通常你并不需要尺寸,例如使用 Boost.Range。

理想情况下,我想将向量的定义和初始化从 main 中提取出来,最好放到一个单独的文件中,然后我可以#include。

这将违反单一定义规则。你可以将声明放到头文件中,但定义必须放到源文件中。

另外,我的印象是我必须手动删除使用 new 创建的任何内容,但它不会使用 delete 或 delete[] 删除 ls。

你的印象是正确的,但你没有用new 创建ls,只有它的元素。使用向量后,您必须delete 它的每个元素,而不是向量本身。

保存多态指针的 STL 容器的推荐替代方法是 Boost pointer container library

【讨论】:

  • 请注意,L* ls = { new A, new B, new C };L ls[] = { new A, new B, new C }; 的细微差别在于前者知道自己的大小(并且您的大小表达式将始终返回 1)。
  • 好的,现在我不确定了。正如 Philipp 所建议的,我已经让它像我想要的常规阵列一样工作。那不安全吗?我应该使用矢量还是提升?
  • @MCXIII:在大多数情况下,向量实际上是一种零开销的解决方案。它不会花费你任何东西。如果数组可以堆栈分配(如果大小在编译时已知并且大小相对较小),那么创建数组比创建向量(总是包含堆分配的数组)更便宜。但在实际使用中,并没有什么区别。一般来说,坚持使用简单且安全的选项(向量),直到您知道自己需要其他方式为止。
  • @MCXXIII:一般来说,C++ 容器的设计与 C 容器一样高效。例如,向量访问(使用相当现代的编译器)应该与 C 数组访问一样快。初始化确实有点贵,但这应该可以忽略不计,因为它只完成了一次。
  • 即使你这样做了,性能也不会受到太大影响:在向量末尾添加元素是一个在摊销常数时间内运行的操作。
【解决方案2】:

您确实必须对您创建的对象使用 delete。您在向量而不是对象上调用删除。比如:

for(size_t i = 0; i < ls.size(); i++){
    delete ls[i];
}

对于您的构造问题,您可以将它们包装成一个函数并将该函数放在它自己的头文件中。您还必须确保包含所有相关的类头文件。

void init_vector(std::vector<LS*> & v){
    ls[0] = new A ; 
    ls[1] = new B ;
    ls[2] = new C ;
}

【讨论】:

  • 现在可以删除了。我对此有点紧张,所以谢谢。关于初始化,真的没有办法做std::vector&lt;L*&gt; ls = {new A, new B, new C} ;之类的事情吗?
  • @MCXXIII 查看 boost assignment 库。 boost.org/doc/libs/1_39_0/libs/assign/doc/index.html这可能会满足您的需求
  • 谢谢。我似乎可以考虑一些解决方案。
【解决方案3】:

如果 C++11 是可以接受的,最好使用 std::array 而不是 std::vector

std::array<L *, 3> = {new A(), new B(), new C()};

【讨论】:

  • 为了便携性,我认为我不应该太“现代”。不过谢谢。
  • C++0x array 应该等同于 Boost 的 array——C++0x 中有不少类模板受 Boost 启发。
【解决方案4】:

由于您在编译时知道大小,我建议使用array 而不是vector。使用类模板 array 而不是 C 样式的数组可以为您提供标准容器接口的好处,就像 vector 一样。即可以在数组上调用size(),获取迭代器等。

为了确保你不会忘记delete 对象,我建议使用智能指针:

#include <boost/array.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

boost::array<boost::shared_ptr<L>, 3> ls = { {
    boost::make_shared<A>(),
    boost::make_shared<B>(),
    boost::make_shared<C>(),
} };

现代编译器在标准库中提供了自己的 arrayshared_ptr 版本:

#include <array>
#include <memory>

std::array<std::shared_ptr<L>, 3> ls = { {
    std::make_shared<A>(),
    std::make_shared<B>(),
    std::make_shared<C>(),
} };

请注意,从技术上讲,最外面的大括号是不需要的,但将它们排除在外可能会产生编译器警告,至少我的编译器会发生这种情况。

理想情况下,我想将向量的定义和初始化从 main 中提取出来,最好放到一个单独的文件中,然后我可以 #include

在这种情况下,你需要一个带有声明的头文件和一个定义ls的实现文件:

// file ls.h

#ifndef LS_H
#define LS_H

#include <boost/array.hpp>
#include <boost/shared_ptr.hpp>

extern boost::array<boost::shared_ptr<L>, 3> ls;

#endif

// file ls.cpp

#include "ls.h"
#include <boost/make_shared.hpp>

boost::array<boost::shared_ptr<L>, 3> ls = { {
    boost::make_shared<A>(),
    boost::make_shared<B>(),
    boost::make_shared<C>(),
} };

【讨论】:

  • 谢谢。而且我似乎永远无法弄清楚定义/声明的内容,我总是把它们混在一起。对此感到抱歉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-30
相关资源
最近更新 更多