Представьте, что у меня есть класс 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;
}

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

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

3
PhysicsPDF 6 Ноя 2019 в 16:02
Да, прости. Просто я работаю с плазмой в ловушке Пеннинга-Мальмберга.
 – 
PhysicsPDF
6 Ноя 2019 в 16:10
Кроме того: обычно и, следовательно, делает код более читабельным, имена class начинаются с заглавной буквы: например class Fish.
 – 
Paul Evans
6 Ноя 2019 в 16:13
В одном аквариуме может быть много разных видов рыб. Различные экземпляры fish или экземпляры разных производных классов fish? В последнем случае std::vector<fish>, вероятно, будет плохим выбором.
 – 
Scheff
6 Ноя 2019 в 16:16
Вместо того, чтобы давать рыбе ссылку на аквариум, я бы предложил добавить к рыбе методы, которые сообщают рыбе, что делать, и аквариум будет вызывать этот метод, предоставляя все необходимые аргументы (т.е. ширина и высота аквариума передаются. чтобы рыба не покидала пределы аквариума)
 – 
Thomas Cook
6 Ноя 2019 в 17:31

4 ответа

Честно говоря, нет правильного или правильного способа сделать это, как если бы поесть риса. Лично мне? В аквариуме будет вектор рыбы, а затем, если рыбе нужно проплыть на 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)

2
Kyle 6 Ноя 2019 в 16:19
1
Fish все равно нужно будет объявить другом в FishTank, верно?
 – 
PhysicsPDF
6 Ноя 2019 в 16:52
1
Если Fish требуется для доступа к private членам tank в Fish::swim, тогда да, Fish должен быть friend из FishTank.
 – 
Paul Evans
6 Ноя 2019 в 17:27

Рыбе необходимо знать свойства fishTank

Тогда вы можете подумать о том, чтобы сделать fish friend из fishTank:

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

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

1
Paul Evans 6 Ноя 2019 в 16:16

Ваш класс 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
ypnos 6 Ноя 2019 в 16:28

Вот мой путь:

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
Ahmed Anter 6 Ноя 2019 в 16:44