在现代 C++ 中使用原始指针是不好的做法吗?
Is this use of raw pointers in modern C++ bad practice?
我想持有一个没有对象切片的 Base class 实例的向量(这样我也可以毫无问题地存储 Base 的子实例)同时保持多态行为而不通过复制值添加到列表, 而不是引用。
考虑以下源文件:
#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.
如输出所示(通过打印出 "player" 并显示 'edited' 中的变化),由于原始指针,多态行为得以保持,我无需编辑列表成员问题。
澄清我的问题:我可以改用 std::reference_wrapper 来实现完全相同的行为吗?当我尝试使用 reference_wrapper 时,无法实现相同的行为,因为实现此多态行为需要指针?如果 reference_wrapper 不是一个可行的替代方案,虽然我非常清楚我添加到向量中的 Player 实例是一个堆栈变量,但使用 shared_ptr 是否明智?在我的特定示例中,我更喜欢 shared_ptr,因为我希望共享向量成员的所有权。有没有更好的方法来实现这种行为?
那里没有错。唯一可能不好的情况是当您开始分配 Entity 对象并忘记删除它们时。
std::vector<Entity*> entities;
entities.push_back(new Player());
// Somthing that may throw exception
delete entities.at(0)
但这是一个有点不同的问题。
非拥有指针在现代 C++ 中很好。但是如果你的指针是 owning,你最好有充分的理由使用原始指针而不是智能指针。即使在最坏的情况下,您仍然应该能够编写自己的智能指针。你的例子是一个非拥有指针。
使用 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
。
我想持有一个没有对象切片的 Base class 实例的向量(这样我也可以毫无问题地存储 Base 的子实例)同时保持多态行为而不通过复制值添加到列表, 而不是引用。
考虑以下源文件:
#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.
如输出所示(通过打印出 "player" 并显示 'edited' 中的变化),由于原始指针,多态行为得以保持,我无需编辑列表成员问题。
澄清我的问题:我可以改用 std::reference_wrapper 来实现完全相同的行为吗?当我尝试使用 reference_wrapper 时,无法实现相同的行为,因为实现此多态行为需要指针?如果 reference_wrapper 不是一个可行的替代方案,虽然我非常清楚我添加到向量中的 Player 实例是一个堆栈变量,但使用 shared_ptr 是否明智?在我的特定示例中,我更喜欢 shared_ptr,因为我希望共享向量成员的所有权。有没有更好的方法来实现这种行为?
那里没有错。唯一可能不好的情况是当您开始分配 Entity 对象并忘记删除它们时。
std::vector<Entity*> entities;
entities.push_back(new Player());
// Somthing that may throw exception
delete entities.at(0)
但这是一个有点不同的问题。
非拥有指针在现代 C++ 中很好。但是如果你的指针是 owning,你最好有充分的理由使用原始指针而不是智能指针。即使在最坏的情况下,您仍然应该能够编写自己的智能指针。你的例子是一个非拥有指针。
使用 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
。