从C ++中另一个(不相关的!)类的基类引用派生类的对象

时间:2015-08-13 07:09:12

标签: c++

如果你从一开始就无法理解问题标题,那不是你的错 - 我想不出更好的描述。以下是对问题的解释,可能有点冗长,所以请提前道歉。

在我的程序的初始版本中,我有一个Ecosystem类和一个Individual类:

// Very simplified, for illustration purposes

class Ecosystem
{
    protected:
        // The int is just the ID of the individual.
        std::map<int, std::shared_ptr<Individual> > individuals;

    public:
        Ecosystem();
        void func(int _individual_id)
        {
            std::cout << "Individual's age: " 
                      << individuals[_individual_id]->get_age() 
                      << std::endl;
        }

        void routine(int _individual_id)
        {
            // Another function working via
            // the pointers in individuals.
        }        

        // More such functions...
};

class Individual
{
    protected:
        int age;

    public:
        Individual();
        inline int get_age() const
        {
            return age;
        }
};

Ecosystem类包含许多功能,我将来会添加更多功能。

我现在决定将Individual类拆分为基类和两个派生类,比如TypeAIndividual和TypeBIndividual,因为它们每个都有成员和属性,而另一个不需要(他们也通过共享一些成员和属性)基类)。所以我有基类Individual类和两个派生类:

class TypeAIndividual : public Individual
{
    protected:
        // Data structures specific to individuals of type A

    public:
        TypeAIndividual();
};

class TypeBIndividual : public Individual
{
    protected:
        // Data structures specific to individuals of type B

    public:
        TypeBIndividual();
};

问题是现在生态系统也需要分为TypeAEcosystem和TypeBEcosystem:

class Ecosystem
{
    protected:
        // Holding pointers to the base Individual class is pointless (pun not intended)
        // std::map<int, std::shared_ptr<Individual> > individuals;

    public:
        Ecosystem();
        // I want to keep func() in the base class
        // because it only accesses attributes and
        // members common to both classes derived
        // from Individual. 
        void func(int _individual_id)
        {
            // Hmmmm...
            // The pointers don't live in the Ecosystem class any more!
            std::cout << "Individual's age: " 
                      << individuals[_individual_id]->get_age() 
                      << std::endl; 
        }
        // OK to implement in each class
        // derived from Ecosystem.
        virtual void routine(int _individual_id) = 0;
};

class TypeAEcosystem : public Ecosystem
{    
    protected:
        // Pointers to individuals
        // of the corresponding type.
        std::map<int, std::shared_ptr<TypeAIndividual> > individuals;

    public:
        TypeAEcosystem();
        // Reimplementing routine() is OK
        // because it does things specific to
        // this individual type.
        virtual void routine (int _individual_id)
        {
            // Operate on data structures particular
            // to this type of individual.
        }

};

class TypeBEcosystem : public Ecosystem
{
    protected:
        // Pointers to individuals
        // of the corresponding type.
        std::map<int, std::shared_ptr<TypeBIndividual> > individuals;

    public:
        TypeBEcosystem();
        // Reimplementing routine() is OK
        // because it does things specific to
        // this individual type.
        virtual void routine (int _individual_id)
        {
            // Operate on data structures particular
            // to this type of individual.
        }
};

TypeAEcosystem和TypeBEcosystem都使用void func(int _individual_id),它需要访问相应类型的个人。但基类Ecosystem不再包含指向个体的指针,因为std::map在每个派生类中而不在基类中。

我的问题是:我如何访问相应类型的个人(TypeAIndividualTypeBIndividual),同时避免在源自生态系统的每个类中实现单独的void func(int _individual_id)?换句话说,有没有办法在基类中保留func(),这样当我更改它时,我不必对派生类进行更改?在实际的程序中,有许多函数,例如func(),只需要int作为参数。此外,其中一些函数从Ecosystem类中的其他结构中获取单个ID,因此我不能简单地将指针传递给TypeAIndividualTypeBIndividual

我考虑过的事情

  • TypeAIndividualTypeBIndividual合并回一个公共Individual类,其中包含两个派生类所需的所有数据结构。这让我觉得这是一种特别笨拙的做事方式,但至少它会起作用。

  • 制作func()&amp;公司虚拟并在TypeAEcosystemTypeBEcosystem中实施它们。这意味着如果我想对任何功能进行更改,我必须更改两种实现(=维护噩梦)。

  • 只有一个Ecosystem类,其中包含两种类型的std::map个人,如下所示:

    // Seems clunky...
    class Ecosystem
    {
        protected:
            // Note: The Ecosystem can contain 
            // one OR the other, but not both!
            // One map will always be empty.
        std::map<int, std::shared_ptr<TypeAIndividual> > type_a_individuals;
    
        std::map<int, std::shared_ptr<TypeBIndividual> > type_b_individuals;
    
        public:
            Ecosystem();
            void func(int _individual_id)
            {
                // Check what type of individuals we 
                // are working with and operate on the
                // appropriate container.
                if (type_a_individuals.size() > 0)
                {
                    std::cout << "Individual's age: " 
                              << type_a_individuals[_individual_id]->get_age() 
                              << std::endl; 
                }
                else
                {
                    std::cout << "Individual's age: " 
                              << type_b_individuals[_individual_id]->get_age() 
                              << std::endl; 
                }
            }
    };
    

这需要在每个函数中插入一个检查,这在可维护性方面几乎与在单独的类中使用函数一样糟糕。

注意:虽然我非常希望避免传递指针,但如果它解决了问题,我会考虑适当地进行向上转换和/或向下转换(作为最后的手段......)。 / p>

欢迎任何建议!

<小时/> 修改1

谢谢大家的精彩回复!正如amit和Chris所建议的那样,看着我的Ecosystem课程,果然,它太笨重了。我将成员函数移动到其他类中,现在我在Ecosystem类中有四到五个基本函数。 Ecosystem类驻留在库中,并提供了与个人进行实验的界面,但我不希望用户能够直接操作Individual和其他类,所以我做不到完全远离它。

我喜欢所有建议,有一些巧妙的解决方案。话虽这么说,克里斯提出的那个立刻引起了我的注意,因为它非常整洁,允许我有一个单独的Ecosystem类而不是三个独立的类(base和two派生)。个人的类型可以在配置文件中指定,我可以在同一个实验中从不同的配置文件中生成多个生态系统。这是公认的答案。

再次感谢大家的建设性意见!

4 个答案:

答案 0 :(得分:0)

查看实现细节,我假设这些。生态系统是个人的容器/处理器类。查看界面,id似乎在不同的个体中是唯一的,即,invidual和B个体不能具有相同的id。 如果这些都是真的,我将坚持一个生态系统类,它定义了访问个体的接口,可以存储在地图中(基本指针*),因为ids是唯一的。然后,如果您想知道正在请求哪种类型,可以使用外部的动态强制转换,并且可以使用各个类的多态接口完成其他操作。我还强烈建议您使用Herb Sutter http://www.gotw.ca/publications/mill18.htm#Notes建议的实施方案,因为许多专家高度承认它的可扩展性。

答案 1 :(得分:0)

您可以在EcoSystem中添加虚拟方法以检索通用TypeIndividual

class EcoSystem
{
public:

    void func(int _individual_id) {
        std::cout << "Individual's age: " 
                  << get_individual(_individual_id).get_age() 
                  << std::endl; 
        }

    virtual const TypeIndividual& get_individual(int _individual_id) const = 0;
    virtual void routine(int _individual_id) = 0;
};

对于每个子类:

class TypeAEcosystem : public Ecosystem
{
protected:
    // Pointers to individuals of the corresponding type.
    std::map<int, std::shared_ptr<TypeAIndividual> > individuals;

public:
    const TypeIndividual& get_individual(int _individual_id) const override
    {
        return *individuals.at(_id);
    }


    // Reimplementing routine() is OK
    // because it does things specific to
    // this individual type.
    void routine (int _individual_id) override
    {
        // Operate on data structures particular
        // to this type of individual.
    }
};

答案 2 :(得分:0)

两个派生Ecosystem&}在存储和访问Individual的方式上有所不同。这使得Individual访问行为成为虚拟的好例子。有一些返回类型的协方差,它看起来很好:

struct Individual                   { void baseStuff()  {} };
struct TypeAIndividual : Individual { void typeAStuff() {} };

struct Ecosystem {

    void func(int id) {
        individual(id).baseStuff();
    }

    virtual void routine(int id) = 0;

protected:
    virtual Individual &individual(int id) = 0;
};

struct TypeAEcosystem : Ecosystem {

    TypeAIndividual &individual(int id) override {
        return *_individuals.at(id);
    }

    void routine(int id) override {
        individual(id).typeAStuff();
    }

private:
    std::map<int, std::shared_ptr<TypeAIndividual>> _individuals;
};

由于地图及其访问者除了个人类型之外是相同的,因此您可以将它们分解为中间模板基类:

template <class Individual>
struct DerivedEcosystem : Ecosystem {

    Individual &individual(int id) override {
        return *_individuals.at(id);
    }

private:
    std::map<int, std::shared_ptr<Individual>> _individuals;
};

struct TypeAEcosystem : DerivedEcosystem<TypeAIndividual> {

    void routine(int id) override {
        individual(id).typeAStuff();
    }
};

答案 3 :(得分:0)

正如我在评论中已经说过的那样,您可以考虑将Ecosystem作为模板化类,并为每个IndivualType设置一个生态系统实例。

template <class IndivualType>
class Ecosystem {
  protected:
    // The int is just the ID of the individual.
    std::map<int, std::shared_ptr<IndivualType> > individuals;
  public:
    // ...
};

如果您需要Ecosystem在给定的IndividualType中表现不同,您还可以明确地专门化您的生态系统,如下所示:

template <>
class Ecosystem<SpecialIndividualType> {
protected:
        // The int is just the ID of the individual.
        std::map<int, std::shared_ptr<SpecialIndividualType> > individuals;
      public:
        // special implementation for EcoSystem for SpecialIndividualType 
};

这可能没有必要,但可能很高兴知道。

最后,如你所说

The Ecosystem class contains dozens of functions, and I will add a lot more in the future.

您可能需要考虑将生态系统的功能划分为策略。我不知道你的需求,但仅作为一个例子:

template <class IndivualType, class SomePolicy1, class SomePolicy2>
class Ecosystem {
  private:
    const SomePolicy1 mSp1;
    const SomePolicy2 mSp2;
  protected:
    // The int is just the ID of the individual.
    std::map<int, std::shared_ptr<IndivualType> > individuals;
  public:
    Ecosystem (const SomePolicy1& sp1= SomePolicy1(), const SomePolicy2& sp2= SomePolicy2())) : mSp1(sp1), mSp2(sp2) {}
    // ...
    void func(int _individual_id) 
        mSp1.doSmth(_individual_id);
    }

    void func2(int _individual_id) {
        mSp2.doSmth(_individual_id);
    }
};

这称为&#34;基于策略的设计&#34;,您可以在网上找到关于它的大量信息。

当然还有其他解决方案,例如如前所述将方法设为虚拟。我可能会尝试两种方式(取决于你的时间),看看你最满意的是什么。