2017-03-03 52 views
4

我想持有一个基类实例的向量,无需对象切片(这样我也可以存储Base的子实例而没有问题),同时保持多态行为而不通过复制值添加到列表中,而是通过引用。这是在现代C++糟糕做法中使用原始指针吗?

考虑以下源文件:

#include <iostream> 
#include <string> 
#include <vector> 
#include <memory> 

class Entity 
{ 
public: 
    Entity(){this->edited = false;} 
    virtual std::string name() = 0; 
    bool edited; 
}; 

class Player: public Entity 
{ 
public: 
    Player(): Entity(){} 
    std::string name(){return "player";} 
}; 

int main() 
{ 
    std::vector<Entity*> entities; 
    Player p; 
    entities.push_back(&p); 
    entities.at(0)->edited = true; 
    Entity* ent = entities.at(0); 
    std::cout << "ent = " << ent->name() << ", edited = " << ent->edited << ".\n"; 
    return 0; 
} 

我得到以下输出:

ent = player, edited = 1. 

作为输出显示(通过打印出“运动员”和表示“编辑”的变化) ,由于原始指针而保持多态行为,并且我能够编辑列表的成员而没有问题。

为了澄清我所问:我可以改为使用std :: reference_wrapper来实现完全相同的行为吗?当我尝试使用reference_wrapper时,无法实现相同的行为,因为需要指针才能实现这种多态行为?如果reference_wrappers不是一个可行的选择,尽管我完全知道我添加到向量中的Player实例是一个堆栈变量,但是使用shared_ptr代替它会是明智的吗?在我特别的例子中,我赞成shared_ptr,因为我希望共享向量成员的所有权。有没有更好的方法来实现这种行为?

+2

我不会说原始指针的* use *必然是坏的。我会说原始指针的显式管理*(即分配,删除)是不被接受的。只是指向一个对象,但不会声称拥有这个内存,不是太糟糕,但是经常有更好的选择。 – CoryKramer

+0

C++中的“不良实践”不仅仅是一种语言,它还与你正在做的事情有关。例如,如果您正在开发一个小型,高效的库,您的准则可能会说使用原始指针来避免两个间接层。如果您正在制作游戏,那么您不需要担心始终分配对象的(分配),因此使用智能指针将是一个很好的实践。我甚至会说在那种情况下,非拥有的raw指针应该被weak_ptr所取代。 – ZeroUltimax

+0

有[std :: observer_ptr](http://en.cppreference.com/w/cpp/experimental/observer_ptr)明确指出你的指针没有拥有。 – Jarod42

回答

3

非拥有指针在现代C++中很好。但是如果你的指针是拥有,你最好有一个很好的理由使用原始指针而不是智能指针。即使在最坏的情况下,你仍然应该能够编写自己的智能指针。你的例子是一个非拥有指针。

使用std::reference_wrapper<T>在这里没有任何收益。我已经包含了你的例子,修改后使用它。请注意,直接操纵向量中的元素会让人感到更烦恼。

int main() 
{ 
    std::vector<std::reference_wrapper<Entity>> entities; 
    Player p; 
    entities.push_back(p); 
    entities.front().get().edited = true; 
    Entity & ent = entities.front(); 
    std::cout << "ent = " << ent.name() << ", edited = " << ent.edited << ".\n"; 
    return 0; 
} 

原始指针只是一个工具,就像C++提供的许多其他工具一样。当它是合适的工具时,不要害怕使用它。现代C++带来的最重大变化是它已经取消了拥有原始指针。经验法则是,如果你必须记住delete你做错了。

请注意,std::vector<T>::front()是获取第一个元素的首选方法,当您知道索引有效时,不鼓励使用std::vector::at

1

这里没有错。唯一可能的情况是,你将开始分配实体对象,并忘记删除它们。

std::vector<Entity*> entities; 
entities.push_back(new Player()); 

// Somthing that may throw exception 

delete entities.at(0) 

但是这是一个有点不同的问题。

+1

我的谢意。在学习现代C++的时候,我错误地认为,根本不鼓励使用原始指针。我现在看到,他们一直都是非常有用的指针。 – Harrand

+1

这段代码并不安全:如果'push_back'抛出,'Player'永远泄漏。 –

相关问题