Как разработать класс, который всегда будет членом другого класса - PullRequest
3 голосов
/ 06 ноября 2019

Представьте, что у меня есть fishTank класс. Этот класс будет представлять аквариум, у него есть размер, некоторые границы, где рыба может быть, некоторые препятствия, где рыба не может быть, некоторая вода течет через аквариум, который перемещает рыбу вокруг, и т. Д.

class fishTank
{
private:
    double length;
    double height;
//And some more complex things that define a fish tank
public:
     fishtank(double, double, ...);
}

Теперь мне нужно добавить рыбу в аквариум. Как правильно объявить мой класс рыбы? (Рыба представляет большую группу отдельных рыб)

  1. Моя рыба всегда будет внутри fishTank, для меня нет смысла иметь одну рыбу.
  2. Рыба должназнать свойства fishTank, все в классе рыбы зависит от аквариума: способ загрузки рыбы в аквариум зависит от размера аквариума, способ перемещения рыбы зависит от размера и свойств ловушки,и т. д.
  3. В одном аквариуме может быть много разных видов рыб.

Моя первая попытка создать класс рыбы и передать аквариум в качестве аргумента в конструкторе рыбы. ,Затем добавьте вектор рыбы в аквариум в качестве частного участника:

class fish
{
private:
    //All properties of fish
public:
    fish(const fishTank& aTank, All other properties of fish);
}


class fishTank
{
private:
    std::vector<fish> fishInTank;
    //Same as before
public:
    //Same as before;
}

Это действительно правильный способ реализации того, что мне нужно? Я просто не думаю, что это хороший дизайн, мне понадобится аквариум в качестве аргумента для рыбы, а затем добавить каждую рыбу в сам аквариум. Имеет ли это смысл?

Будет ли это правильным местом для реализации чего-то вроде абстрактного класса рыб или гнезда рыбы внутри аквариума?

Ответы [ 4 ]

2 голосов
/ 06 ноября 2019

Честно говоря, точно так же, как есть Рис, нет правильного или правильного способа сделать это. Лично я? В аквариуме может быть вектор рыбы, и затем, если рыбе нужно плыть в течение 1 тика, у меня будет метод плавания в аквариуме. Это вызвало бы каждую рыбу и велело им плавать, передавая аквариум в качестве аргумента их метода плавания. Примерно так:

class FishTank;

class Fish {
    public:
        void swim(const FishTank* tank) {

        }
};

class FishTank {
    private:
        std::vector<Fish> allFish;

    public:
        void tick() {
            for(Fish& fish : allFish){
                fish.swim(this);
            }
        }
};

На самом деле существует полтора десятка способов сделать это. Есть книга, которую я бы порекомендовал почитать, и в ней есть что-то вроде этого: Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (ISBN 10: 0201633612)

1 голос
/ 06 ноября 2019

Вот мой путь:

class FishTank;
class Fish
{
protected:
    FishTank* tank;
    Fish(FishTank* t) { tank = t; };
public:
    void swim();

    friend class FishTank;
};

class FishTank
{
private:
    std::vector<Fish*> allFish;
public:
    Fish* AddFish() {
        Fish* f = new Fish(this);
        allFish.push_back(f);
        return f;
    }
    void tick() {
        for (Fish* fish : allFish) {
            fish->swim();
        }
    }
    friend class Fish;
};
1 голос
/ 06 ноября 2019

Ваш класс Fish может быть объявлен внутри класса FishTank, даже защищенным или закрытым. Это сделает его более трудным / невозможным для использования вне Fish. Но нет никакого способа диктовать, что он может быть только прямым членом FishTank.

class FishTank {
private:
    class Fish {
        Fish(const Fishtank &parent);
        …
    };
    std::vector<Fish> fishInTank;

Когда класс объявлен внутри, он автоматически становится другом FishTank и может получить доступ ко всем членамтанк, включая частных участников.

Как отмечает Пол Эванс, вы также можете использовать обозначение friend для достижения последнего эффекта. Полезно, если Fish является сложным классом, который загромождает объявление класса FishTank и его лучше хранить в другом заголовочном файле. Fish по крайней мере нужно объявить публичным, если ваш FishTank когда-либо выставляет рыб на улицу;также для любой стороны, которая имеет дело с Fish, может быть обременительным включать полный заголовок FishTank.

Вы также можете работать с пространством имен для объяснения семантической группировки:

namespace fish_tank {
    class Fish { … };
    class Tank { … };
};
1 голос
/ 06 ноября 2019

Рыба должна знать свойства fishTank

Тогда вы можете рассмотреть вопрос о создании fish friend из fishTank:

class fishTank
{
private:
    friend fish;
    std::vector<fish> fishInTank;
...

теперь любой экземпляр fish может получить доступ ко всем private членам любого данного экземпляра fishTank.

...