Inheritance 实现C++中双向可见性的推荐方法 我想知道,在C++ 14中,类实例及其成员之间的双向可见性的推荐方法是什么?

Inheritance 实现C++中双向可见性的推荐方法 我想知道,在C++ 14中,类实例及其成员之间的双向可见性的推荐方法是什么?,inheritance,constructor,reference,c++14,smart-pointers,Inheritance,Constructor,Reference,C++14,Smart Pointers,作为一个例子,让我们假设我想要编写一个由不同类型的人组成的类世界。所以世界上有一个std::vector>。Person应该是一个抽象基类,在PersonOne和PersonTwo中实现纯虚拟函数。现在,我想允许个人实例能够访问他们的世界,包括其他人 我的第一种方法是在世界的构造函数中传递世界作为对它的引用。然而,我不确定这是否会给我带来麻烦。理想情况下,我希望有这样一个自包含的世界,即世界中包括人在内的世界应该在世界的构造函数中完全初始化,并且应该可以在不单独调整人的属性的情况下复制构建世界。

作为一个例子,让我们假设我想要编写一个由不同类型的人组成的类世界。所以世界上有一个std::vector>。Person应该是一个抽象基类,在PersonOne和PersonTwo中实现纯虚拟函数。现在,我想允许个人实例能够访问他们的世界,包括其他人

我的第一种方法是在世界的构造函数中传递世界作为对它的引用。然而,我不确定这是否会给我带来麻烦。理想情况下,我希望有这样一个自包含的世界,即世界中包括人在内的世界应该在世界的构造函数中完全初始化,并且应该可以在不单独调整人的属性的情况下复制构建世界。智能指针似乎不是一个可行的选择,因为我无法在世界的构造函数中创建一个共享的ptr,并将其传递给他们的成员

class Person{
    public:
    World &world;

    Person(World &_w):world(_w){};
};

class PersonOne: public Person{

    public: 
    PersonOne(World &_w):Person(_w);
};

class PersonTwo: public Person{

    public: 
    PersonTwo(World &_w):Person(_w){};

};

class World{
    public:
    std::vector<std::unique_ptr<Person>> people;

    World(){
        people.push_back(std::make_unique<PersonOne>(*this));
        people.push_back(std::make_unique<PersonTwo>(*this));
    }; 
};

在这种情况下,复制是很棘手的,因为您必须始终确保人员引用正确的世界。一个选项是确保在World中有自定义的复制构造函数和operator=,您可以在其中调用函数来更新填充。大概是这样的:

#include <iostream>
#include <vector>
#include <string>
#include <functional>

using uint = unsigned int;

class World;
using WorldRef = std::reference_wrapper<World>;

class Person
{
    WorldRef world;
    std::string name;
    friend class World;
public:

    Person(World& _world,
           const std::string& _name)
        :
          world(_world),
          name(_name)
    {}
};

class World
{
    uint year;
    std::string name;
    std::vector<Person> population;
    friend class Person;

    void init()
    {
        for (auto& p : population)
        {
            /// Note: This is necessary, otherwise this world's
            /// inhabitants will refer to the wrong world.
            /// Call this function from each constructor and
            /// assignment operator.
            p.world = std::ref(*this);
        }
    }

public:

    World(const uint _year,
          const World& _other,
          const std::string& _name)
        :
          year(_year),
          population(_other.population),
          name(_name)
    {
        init();
        std::cout << "It is now " << year << "\n"
                  << "Melon Tusk has built a rocket allowing cheap interplanetary travel.\n";
        for (auto& p : population)
        {
            std::cout << p.name << " has moved to " << p.world.get().name << "\n";
        }
    }

    World(const uint _year,
          const std::string& _name,
          const uint _pop_size)
        :
          year(_year),
          name(_name),
          population(_pop_size, {*this, "Anonymous " + _name + "ling"})
    {
        init();
        std::cout << "It is " << year << "\n";
        std::cout << name << " has " << population.size() << " inhabitants:\n";
        for (const auto& p : population)
        {
            std::cout << p.name << "\n";
        }
    }

    void operator = (const World& _other)
    {
        population = _other.population;
        init();
    }

    void set_name(const std::string& _name)
    {
        name = _name;
    }

    std::string get_name() const
    {
        return name;
    }

    void add_person(const std::string& _name)
    {
        population.emplace_back(*this, _name);

        std::cout <<_name << " was born on " << name << "\n";
    }

    uint population_size() const
    {
        return population.size();
    }
};

int main()
{
    World earth(2018, "Earth", 5);
    World mars(2025, earth, "Mars");

    mars.add_person("Alice");
    mars.add_person("Bob");
    mars.add_person("Carol");

    World europa = mars;
    europa.set_name("Europa");

    std::cout << "Everyone has moved on to " << europa.get_name() << "\n";

    europa.add_person("David");

    std::cout << europa.get_name() << " has a population of " << europa.population_size() << "\n"
              << "A war is brewing over scant resources.\n"
              << "Such is the nature of humanity.\n";

    return 0;
}
注意我们如何在每个构造函数和运算符中调用init=。根据您的需要,可能会有一种更整洁的方法


另外,我不确定从逻辑的角度复制一个世界对象意味着什么,因为你最终会克隆出你的人。也许这正是您想要的,但我只是想提一下。

问题可能是当您从World继承另一个类时。请亲自使用std::reference\u包装器作为成员。您可以将其传递给Person的构造函数,然后使用std::ref亲自初始化std::reference_包装器成员。@cantordust:这是一种什么改进?到目前为止,我认为std::reference\u包装器主要用于引用,例如容器中的引用,所以我看不出这里的用途,我可以只使用普通引用。@Chris您可以重新分配std::reference\u包装器,但不能使用普通引用。这反过来又允许你复制你的世界。此外,只需将World&传递给Person的构造函数,就可以创建std::reference_包装器。正如您所提到的,您不能像那样创建std::shared_ptr;我想到了这一点,但它可能是好的,其他人有它作为参考。是的,计划是克隆不同的世界,包括里面的人来测试反事实。@Chris听起来像是完全的回忆:
It is 2018
Earth has 5 inhabitants:
Anonymous Earthling
Anonymous Earthling
Anonymous Earthling
Anonymous Earthling
Anonymous Earthling
It is now 2025
Melon Tusk has built a rocket allowing cheap interplanetary travel.
Anonymous Earthling has moved to Mars
Anonymous Earthling has moved to Mars
Anonymous Earthling has moved to Mars
Anonymous Earthling has moved to Mars
Anonymous Earthling has moved to Mars
Alice was born on Mars
Bob was born on Mars
Carol was born on Mars
Everyone has moved on to Europa
David was born on Europa
Europa has a population of 9
A war is brewing over scant resources.
Such is the nature of humanity.