C ++容器中充满了抽象对象

时间:2015-09-24 19:57:44

标签: c++ stl

我的背景主要是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的实例。从我读过的各个地方,

  • 我应该使用指针以使多态性正常工作
  • 几乎不需要C ++指针,因为它们不是必需的,当你需要时,自动指针几乎总是更好,通常你可以使用引用
  • struct s(例如类)应该通过引用传递,而不是复制并从函数返回
  • 完全可以在C ++中返回完整的对象

它开始觉得有点像C ++希望我使用C的工具来使用Java的内存实践,而且它真的不起作用。在这一点上,我很想在窗口外抛出任何涉及自动内存分配的最佳实践,并将我的代码丢入*,但我想听听如果有&#34;对&#34;首先这样做的方式。

4 个答案:

答案 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成员。因此,为了存储此层次结构的任何对象(即BaseDerived),您现在至少需要sizeof(Derived)个字节。这是:

  1. 浪费
  2. 不容易实现,因为您的类层次结构可以分散在不同的翻译单元中(您可以拥有另一个派生自Base并具有其他类成员的类)
  3. 这两个问题都可以通过间接解决。您可以存储大小固定的东西,但可以指向不同大小的对象,而不是存储对象本身。例如,一个指针。最好是一个聪明的人。

    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;
}