我想我对 C++ 相当了解,并且我正在考虑实现比“玩具”程序更大的东西。我知道堆栈内存和堆内存与 RAII 成语之间的区别。
假设我有一个简单的类点:
class point {
public:
int x;
int y;
point(int x, int y) : x(x), y(y) {}
};
我总是在堆栈上分配点,因为对象很小。因为在 64 位机器上sizeof(point) == sizeof(void*)
,如果没有错,我会走得更远,默认情况下按值传递分数。
现在让我们假设一个更复杂的职业战场,我想在职业游戏中使用它:
class battlefield {
public:
battlefield(int w, int h, int start_x, int start_y, istream &in) {
// Complex generation of a battlefield from a file/network stream/whatever.
}
};
因为我真的很喜欢 RAII 和对象离开范围时的自动清理,所以我很想在堆栈上分配战场。
game::game(const settings &s) :
battlefield(s.read("w"), s.read("h"), gen_random_int(), gen_random_int(), gen_istream(s.read("level_number"))) {
// ...
}
但是我现在有几个问题:
由于这个类没有零参数构造函数,我必须在我使用战场的类的初始化列表中对其进行初始化。这很麻烦,因为我需要来自某个地方的 istream。这导致了下一个问题。
复杂的构造函数有时会“滚雪球”。当我在游戏类中使用战场并在初始化列表中初始化游戏的构造函数时,游戏的构造函数也会变得相当复杂,游戏本身的初始化也可能变得繁琐。(当我决定将istream作为游戏构造函数的参数时)
我需要辅助函数来填写复杂的参数。
我看到这个问题的两种解决方案:
要么我为不初始化对象的战场创建一个简单的构造函数。但是这种方法的问题是我有一个半初始化的对象,也就是一个违反 RAII 习惯用法的对象。在这样的对象上调用方法时可能会发生奇怪的事情。
game::game(const settings &s) { random_gen r; int x = r.random_int(); int y = r.random_int(); ifstream in(s.read("level_number")); in.open(); this->battlefield.init(s.read("w"), s.read("h"), x, y, in); // ... }
或者我在游戏构造函数的堆上分配战场。但是我必须小心构造函数中的异常,并且我必须注意析构函数会删除战场。
game::game(const settings &s) { random_gen r; int x = r.random_int(); int y = r.random_int(); ifstream in(s.read("level_number")); in.open(); this->battlefield = new battlefield(s.read("w"), s.read("h"), x, y, in); // ... }
我希望你能看到我在想的问题。我遇到的一些问题是:
我不知道这种情况是否有设计模式?
大型 C++ 项目的最佳实践是什么?哪些对象在堆上分配,哪些在栈上分配?为什么?
关于构造函数的复杂性的一般建议是什么?对于构造函数来说,从文件中读取太多了吗?(因为这个问题主要来自复杂的构造函数。)