【问题标题】:Dynamic allocation of vectors in a class类中向量的动态分配
【发布时间】:2013-12-08 00:56:25
【问题描述】:

如果我有一个包含另一个类的向量的类,并且我希望它很长:

class NucleotideSequence{
private:
    std::string Name;
    std::vector<Nucleotide> Sequence;
public:
    NucleotideSequence();
    NucleotideSequence(std::string name, std::vector<Nucleotide> seq);
    std::string getName();
    Nucleotide* getBase(int pos1);
    int getLength();
    void print();
};

在这种情况下是向量序列,我是否需要通过在构造函数中创建序列 *Sequence 并创建一个新向量来动态分配它?我想确保为大型向量(超过数十万个元素)使用正确的资源(堆栈与堆)。哪个是正确的做法?我听说向量包装了动态数组分配。

编辑:

我在下面提供了更新的代码,以表明我已经为构造函数使用了引用传递。我也希望使用移动构造函数,这样我就可以在一个函数中创建这些对象,然后将它们移到外面。

还给出了更新的 getPos 方法,如果该位置在序列中不存在,则会引发错误。

class NucleotideSequence{
private:
    std::string Name;
    std::vector<Nucleotide> Sequence;
public:
    NucleotideSequence();
    NucleotideSequence(const std::string &name, const std::vector<Nucleotide> &seq); // Note that a pointer is not needed since the std::vector class allocated memory on the heap for us and is a wrapper for that whole RAII process.
    std::string getName();
    Nucleotide getBase(int pos);
    int getLength();
    void print();
};

NucleotideSequence::NucleotideSequence(const std::string &name, const std::vector<Nucleotide> &seq)
{
    Name = name;
    Sequence = seq;
}

// Get a specific base
Nucleotide NucleotideSequence::getBase(int pos)
{
    for(std::vector<Nucleotide>::iterator i = Sequence.begin(); i != Sequence.end(); i++)
    {
        if(pos == i->getPos())
        {
            return *i; // Return the correct nucleotide object.
        }
    }
    throw BoundsError(); // If the desired position is not found, throw the error.
}

谢谢, 本。

【问题讨论】:

    标签: c++ class vector dynamic-memory-allocation


    【解决方案1】:

    我会说最好将您的变量成员Sequence 保留为std::vector(而不是pointerstd::vector)。正如您提到的“向量包装动态数组分配”:std::vectorRAII 方式为您管理内存(堆分配/释放/重新分配):
    当你写:std::vector&lt;Nucleotide&gt; SequenceSequence 将对象Nucleotide 存储在heap 上(不在stack 上)

    一个建议:在您的构造函数中,您通过值传递std::vector(以及std::string)。如果您的std::vector 的大小很大,则按值传递会很昂贵。您需要考虑是否可以在您的情况下应用引用传递。

    【讨论】:

      【解决方案2】:

      所有vectors 都在堆上并动态分配。你声明它的方式很好,但你可能应该在你的构造函数中初始化它。

      NucleotideSequence...我打赌这是一个很大的数组。

      【讨论】:

      • 除了与您的问题无关:Nucleotide 是一种微小的数据类型(只要您不绕过芳香环中电子的量子态),因此您可能想要返回它的值来自getBase。这将避免 vector 被重新分配以适应更大的序列并且所有指向旧序列的指针都失效的问题。
      • 当我用 getPos() 函数返回一个指针时,如果位置不存在,我返回一个 nullptr,如果我必须返回一个核苷酸,我不确定如何处理用户进入一个不存在的位置。我想在这种情况下,如果我返回该值,我可以向 stderr 发送一条消息并返回一个空的核苷酸对象或一个帽子编码“N”。
      • @Ward9250 您已将长度提供给客户端代码,因此抛出异常以尝试访问越界基数并没有错。这样你会得到更好的错误信息。更进一步:我希望您使用的语言具有通用Object.clone。那会是一个很好的笑话。
      • 你说得对,它很可能是一个大阵列——我正在为一个关于基因组序列等大序列的研究项目构建一个分析仪。它从序列中删除了尽可能多的不必要的非信息性信息,但我想知道我是否需要从上面编写的类中移出 - 核苷酸包含碱基和位置(char 和 int),并且 NucleotideSequence 是一个向量核苷酸和具有序列名称的字符串。我已经将核苷酸编码为两位,但感觉很复杂,而且本身很容易成为另一个问题。
      • @Ward9250 使用2位会很复杂;还有一个额外的好处是能够使用位操作来快速获得免费的基础,但相对于让 C++ 不透明地使用 2 位类型所必须做的体操,这并不是很多回报。如果您的程序需要扩展以处理 RNA、甲基化等,那么这将翻倍。不过,可能有利于存储在磁盘上。
      【解决方案3】:

      是的,包装了整个动态分配的数组。 您不需要动态分配一个,这样做通常是错误的。只需像您一样将它们声明为直接数据成员即可。

      但是,向量使用值语义,因此这样做有一个潜在的缺点,即生成的类的复制/分配成本非常高(就像大型向量的复制/分配成本高昂一样)。

      这通常很好,它只是让它成为一个大的东西,而大的东西在传递它们时通常只是通过引用传递,所以确保你通过引用传递你的序列,而不是通过值传递它。 (当然,你没有提供 copy-ctr/assignment 操作符,所以你很可能会这样做)

      【讨论】:

      • 嗨,我确实打算在类中添加一个移动构造函数和一个复制构造函数,尤其是在看到 Bjarne 关于 C++ 风格的演讲以及将大型或复杂变量移动到函数之外之后的移动构造函数,而不是而不是按值返回。为什么我需要重载赋值运算符? (自从学习 C++ 以来,我读过的主要是构造函数和析构函数,所以这些其他东西对我来说是新的)。我知道运算符重载类,但还没有使用它。
      猜你喜欢
      • 2020-06-15
      • 1970-01-01
      • 1970-01-01
      • 2022-01-10
      • 2020-06-20
      • 2017-03-10
      • 2021-05-05
      • 2016-08-11
      • 2014-06-16
      相关资源
      最近更新 更多