我的背景主要是C和Java(还有其他一些东西),并且已经开始使用C ++开发一个项目了。我的确切任务是从配置文件生成一组对象(即,读取文件,确定要创建的对象类型,将其添加到列表中,重复)。有问题的部分等同于以下内容:
(我使用C char*
语法,因为我可以在这里轻松编写示例 - 假设我实际上并不知道我需要多少,这就是为什么我会喜欢使用vector
)
class General {
public:
virtual int f()=0;
};
class SpecificA : public General {
public:
virtual int f();
};
class SpecificB : public General {
public:
virtual int f();
};
std::vector<General> build(char** things, int number) {
std::vector<General> result;
for(int i=0;i<number;i++) {
if(strcmp(things[i],"A")) {
result.push_back(SpecificA());
} else if(strcmp(things[i],"B")) {
result.push_back(SpecificB());
}
}
return result;
}
这不会奏效,因为我无法vector
General
,因为您实际上永远不会有General
的实例。从我读过的各个地方,
struct
s(例如类)应该通过引用传递,而不是复制并从函数返回它开始觉得有点像C ++希望我使用C的工具来使用Java的内存实践,而且它真的不起作用。在这一点上,我很想在窗口外抛出任何涉及自动内存分配的最佳实践,并将我的代码丢入*
,但我想听听如果有&#34;对&#34;首先这样做的方式。
答案 0 :(得分:4)
如评论中所述,您可以通过以下方式使用unique_ptr:
std::vector<std::unique_ptr<General>> build(char** things, int number) {
std::vector<std::unique_ptr<General>> result;
for(int i=0;i<number;i++) {
if(strcmp(things[i],"A")) {
result.push_back(std::unique_ptr<General>(new SpecificA()));
} else if(strcmp(things[i],"B")) {
result.push_back(std::unique_ptr<General>(new SpecificB()));
}
}
return result;
}
答案 1 :(得分:2)
代码中的主要问题是您尝试直接使用多态对象,而不是通过指针或引用。而C ++并没有这样做。
C ++中的对象具有(其中包括)大小。这个尺寸是固定的,不会改变。所有C ++机器都是围绕这个想法而建立的。类A
的对象占用sifeof(A)
个字节。类A
的n个对象的数组占用n * sizeof(A)
个字节,因为它包含n个连续分配的类型为A
的对象。具有这种固定大小使指针算术和常时成员访问操作符成为可能。
现在,层次结构中不同类的对象可以具有不同的大小。这是一个例子:
struct Base {
virtual void f() { std::cout << "Base\n"; }
};
struct Derived: Base {
void f() override { std::cout << "Derived\n"; }
int m_value = 0;
};
sizeof(Base) != sizeof(Derived)
非常明显,因为后者拥有m_value
成员。因此,为了存储此层次结构的任何对象(即Base
或Derived
),您现在至少需要sizeof(Derived)
个字节。这是:
Base
并具有其他类成员的类)这两个问题都可以通过间接解决。您可以存储大小固定的东西,但可以指向不同大小的对象,而不是存储对象本身。例如,一个指针。最好是一个聪明的人。
Java和C ++之间的区别在于Java自动为您使用间接,而C ++希望您自己这样做。只要您使用智能指针,您就不会违反任何涉及自动内存分配的最佳实践&#34;。
TL; DR您的最佳做法是使用std::vector<std::unique_ptr<General>>
。
答案 2 :(得分:1)
那是胡说八道。相反,你应该总是以和的价值返回,除非有充分的理由不这样做。对于记录,这也适用于字符串 - 我知道你说你使用大型结构(如类)应该通过引用传递 而不是复制并从函数返回
char*
来方便写这个问题,但对于这个问题的未来读者,不要这样做在你的实际代码中。始终使用std::string
。
一个简单的智能指针(如Ilya Kobelevskiy的答案)足以解决这个问题。
还有其他解决方案。例如,您可以改为使用函数并迭代对象,但这并不是必需的。
template<typename T> void build(std::vector<std::string> things, T func) {
for(auto str : things) {
if(str == "A") {
func(SpecificA());
} else if(str == "B")) {
func(SpecificB());
}
}
}
现在你可以将它用作lambda。
int main() {
auto things = /* insert things here */;
build(things, [](const General& g) {
// Don't miss the const, it's kinda important.
g.f();
});
}
现在你甚至不知道如何分配事物,因为它们只是价值观。这显然不完全等同于您之前的情况,但可能足够接近,具体取决于您的实际用例。
C ++真的不像C或Java,如果你试图想象它是,那么你就会直到你停下来。
答案 3 :(得分:-1)
您需要在堆上创建对象并将指针存储在集合中。像那样:
std::vector<General*> build(char** things, int number) {
std::vector<General*> result;
for(int i=0;i<number;i++) {
General* newObj = nullptr;
if(strcmp(things[i],"A")) {
newObj = new SpecificA();
} else if(strcmp(things[i],"B")) {
newObj = new SpecificB();
}
if(newObj != nullptr){
result.push_back(newObj); // you can do the "push" in one place
}
}
return result;
}
或者,使用std::unique_ptr
:
std::vector< std::unique_ptr<General> > build(char** things, int number) {
std::vector< std::unique_ptr<General> > result;
for(int i=0;i<number;i++) {
std::unique_ptr<General> newObj;
if(strcmp(things[i],"A")) {
newObj = std::unique_ptr<General>(new SpecificA());
} else if(strcmp(things[i],"B")) {
newObj = std::unique_ptr<General>(new SpecificB());
}
if(newObj.get() != nullptr){
result.push_back(newObj); // you can do the "push" in one place
}
}
return result;
}