【问题标题】:c++ vector without pointers没有指针的c++向量
【发布时间】:2015-01-24 04:36:40
【问题描述】:

我有一个std::vector<Enemy*> enemies,当我创建一个新的Enemy 时,我会使用enemies.push_back(this)。这适用于我正在做的事情,但我想知道是否有办法让向量不需要指向敌人的指针。我只尝试过std::vector<Enemy>,但它似乎导致了一堆我无法弄清楚的问题。

即。 - 不得不更改为enemies.push_back(*this),并且由于某种原因导致了一些链接器错误。

循环执行以下工作,但删除指针会产生问题:

void Enemy::updateEnemies(sf::RenderWindow &window) {
    for(std::vector<Enemy*>::iterator it = enemies.begin(); it < enemies.end(); it++) {
        (*it)->update(window);
    }
}

想知道是否有人可以指出我使用没有指针的向量并循环遍历它,使用相同的函数更新其中的对象..

【问题讨论】:

  • 你为什么要做push_back(this),是什么让你相信解决方案是push_back(*this)?您的问题中缺少这一点...在 C++11 中请使用智能指针。
  • 这不是您向矢量添加内容的方式吗?来自 javascript,array.push() 通常是我做事的方式。
  • 要让vector&lt;Enemy&gt; 工作,您的课程必须是可复制的。 “一堆我想不通的问题”表明您的班级在复制时表现不佳。它应该正常运行,或者禁用复制。谷歌“三规则”(或“五规则”)。
  • 我的水晶球告诉我你的Enemy 类中有std::vector&lt;Enemy*&gt;
  • 对象将自身添加到数据结构中是不寻常的。这个enemies 变量在哪里?是static 还是global?对象是否在其析构函数中移除自身?

标签: c++ pointers c++11 vector


【解决方案1】:

每个人都在 cmets 中这么说 - 但它的答案

使用智能指针

如果你不知道怎么做,那么谷歌

您的生活更轻松、更安全、更快捷,...

而且它们真的很容易使用 - 使用它们的主要技巧是信任它们,不要试图变得聪明或“帮助”它们

【讨论】:

    【解决方案2】:

    我的做法是确保该类实现(并使public)以下所有内容:默认构造函数、复制构造函数、赋值运算符和析构函数。那么std::vector&lt;Enemy&gt;之类的就没有问题了。 (向量甚至不需要默认构造函数,但可以很方便地处理各种其他事情。)

    但有一点需要注意:当您将 push_back()Enemy 放入向量中时,它实际上是存储的原始实例的副本。这引发了一些问题:首先,对原始文件的更改(例如,在下面我的示例的main() 范围内更改变量e 的成员数据)不会影响存储在向量中的副本。如果您习惯于使用指针向量,这可能会令人惊讶。其次,副本的隐式构造和成员数据的复制需要时间,这可能会对性能产生重大影响,也可能不会。第三,隐式构造和复制可能会引发副作用(您应该在设计中尽可能避免这些)。

    这是一个例子:

    #include <vector>
    #include <iostream>
    
    class Enemy
    {
      public:
        Enemy() : mEvilness( 1 ) {}    // default (i.e. parameterless) constructor
        Enemy( const Enemy& other ) { Copy( other ); } // copy constructor
        Enemy& operator=( const Enemy& other ) { return Copy( other ); } // assignment operator
        ~Enemy() {}  // destructor
    
        Enemy( int evilness ) : mEvilness( evilness ) {}  // standard constructor
    
        void Gloat( void ) const
        {
            std::cout << "muahaha";
            for( int i = 0; i < mEvilness; i++ ) std::cout << "ha";
            std::cout << "!\n";
        }
    
        int mEvilness;
    
      private:
    
        // common helper method for copy constructor and assignment operator:
        Enemy& Copy( const Enemy& other )
        {
            mEvilness = other.mEvilness; // make copies of any other member data here
            return *this;
        } 
    };
    
    
    typedef std::vector< Enemy > EnemyHorde;
    
    void AllGloat1( const EnemyHorde& h ) // doesn't matter if you pass by reference...
    {
        for( EnemyHorde::const_iterator it = h.begin(); it != h.end(); it++ )
            it->Gloat();
    }
    
    void AllGloat2( EnemyHorde h ) // ...or value (but this will take more CPU time)
    {
        for( EnemyHorde::const_iterator it = h.begin(); it != h.end(); it++ )
            it->Gloat();
    }
    
    int main( int argc, const char* argv[] )
    {
        EnemyHorde h;
        Enemy e( 1 );
        h.push_back( e );
        h.push_back( Enemy( 2 ) ); // even transient Enemy instances get copied and stored
        h.push_back( Enemy( 3 ) );
    
        AllGloat1( h );
        std::cout << "\n";
    
        e.mEvilness++;  // this will have no effect on the output because h contains a *copy* of e
    
        AllGloat2( h );
    
        return 0;
    }
    

    【讨论】:

    • vector&lt;Enemy&gt; 的编译不需要默认(无参数)构造函数。
    • 很高兴知道。我想我一直养成包含一个的习惯,因为在很多其他情况下它是有帮助的(比如Enemy * Sparta = new Enemy[300];)。在我上面的示例中,构造函数定义当然也可以通过使用默认值来缩写,这通常是最有意义的:Enemy( int evilness=1 ) : mEvilness( evilness ) {}
    【解决方案3】:

    如果您想要一个有根据的答案,您必须退后一步 - 回到设计。据我所知, Enemy 对象正在通过构造函数在一种全局列表中注册自己。到目前为止一切顺利。

    但是这些对象是如何产生的呢?在这个例子中 Enemy 可以在任何地方构造

    • 在堆栈上作为本地
    • 由新的(智能指针)
    • 作为向量、列表、双端队列中的元素...
    • ...

    基于此,Enemy-object 无法对其存储做出任何可靠的陈述。但是,所有对象共享且始终可以从对象内部访问的一件事是地址(“this”)。因此,通过指针的解决方案是唯一对所有构造的 Enemy-objects 通用的解决方案。 (在这种情况下,引用也是“一种指针”,其他更复杂的构造也可能依赖于指针)。

    一个 std::vector 不能工作,因为你得到 2 个不同的对象。改变其中一个不会影响另一个。那是行不通的。在不改变设计的情况下,不可能切换到智能指针。

    有两种常见的解决方案:

    以与给定示例类似的一般方式从“内部”敌人管理列表。可扩展更多功能,例如通过析构函数自动删除条目.....

    从“外部”管理列表。 Enemy-objects 不注册自己,Enemy-objects 的生成器负责正确地进行注册(在这种情况下,设计模式“工厂”是一个很好的解决方案)。而且您可以使用智能指针,这在大多数情况下是个好主意。

    还有一些未提及的解决方案(包括一些好的解决方案),它们超出了问题的范围。

    【讨论】:

      【解决方案4】:

      在 C++ 中(大多数时候)您不需要指针。

      使用std::vector::push_back() 方法时会进行复制。

      例子:

      Enemy evil_wizard;
      std::vector<Enemy> enemy_container;
      enemy_container.push_back(evil_wizard);
      Enemy Evil_knight;
      enemy_container.push_back(Evil_knight);
      Enemy pink_elephant;
      enemy_container.push_back(pink_elephant);
      

      【讨论】:

        猜你喜欢
        • 2020-04-13
        • 2019-09-10
        • 1970-01-01
        • 1970-01-01
        • 2011-08-04
        • 2015-06-21
        • 2017-08-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多