Одиннаковые имена полей разных классов.

Модератор: Модераторы разделов

iAm
Сообщения: 220
ОС: Gentoo

Одиннаковые имена полей разных классов.

Сообщение iAm »

Здравствуйте.
Изучаю C++. :)

У меня есть три класса:

Код: Выделить всё

// Первый класс - юниты.
class Unit{
    public:
        char* name; // Unit's name.
};

// Второй класс - оружие.
class Weapon{
    public:
        char* name; // Weapon's name.
};

//И, наконец, третий класс - наследник первых двух.
class Tank: public Unit,
            public Weapon{

    // ...
};


Далее я делаю так:

Код: Выделить всё

Tank *tanks = new Tank;

tanks[0].name = "MegaWeapon";

И, естесственно, компилятор ругается.
Я-то понимаю, Tank наследует все поля от предков. А у наследуемых предков есть поле с одинаковым именем name.

Я решил обойти проблему путем добавления префиксов к полям.
То есть в классе Unit я поменял название поля name на u__name, в классе Weapon - на w__name.
Все работает, но я бы хотел узнать, существует ли более "элегантное" решение этой "проблемы"? :)
Спасибо сказали:
-error
Сообщения: 174
Статус: sysadmin / oracle dba
ОС: HP-UX :-)

Re: Одиннаковые имена полей разных классов.

Сообщение -error »

хм. тот же g++ прямо дает подсказку: уточнить область видимости.

Код: Выделить всё

[sergey@freebsd ~/work]$ g++ -c tst.cpp
tst.cpp: In function `int main()':
tst.cpp:20: error: request for member `name' is ambiguous
tst.cpp:9: error: candidates are: char*Weapon::name
tst.cpp:4: error:                 char*Unit::name

т.е. что-то в роде tanks[0].Unit::name = "MegaWeapon";
Спасибо сказали:
sdk
Бывший модератор
Сообщения: 210

Re: Одиннаковые имена полей разных классов.

Сообщение sdk »

Во-первых, убери name из public, положи его в private. Возьми за правило - не делать public и protected данных. Потом будет проще ;).

Во-вторых, у тебя неправильный дизайн. public наследование от Weapon здесь не нужно. Нужно включение. А name - достаточно иметь только в классе Unit - Tank его унаследует.

Вот так, например

Код: Выделить всё

class Unit
{
public:
        Unit() {}
        void setName(const string& name) { m_name = name; }
        string name() const { return m_name; }
private:
   string m_name;
};

class Tank : public Unit
{
     public:
         Tank() { m_weapon=new Weapon; }
     private:
         Weapon *m_weapon;
};


public-наследование нужно для отношений типа IS-A, то есть, когда ты моделируешь нечто, что ЯВЛЯЕТСЯ тем, от чего ты наследуешься. Танк не ЯВЛЯЕТСЯ оружием - это одна из его составляющих :).
Пардон за сумбурность, у меня в голове это все на английском :).
Серьезность - это способ сделать простые вещи сложными.
Если много знать - устанут глаза. Если много спать - то нет.
Нас никому не сбить с пути - нам пофигу куда идти.
:-)
Спасибо сказали:
iAm
Сообщения: 220
ОС: Gentoo

Re: Одиннаковые имена полей разных классов.

Сообщение iAm »

(sdk @ Dec 3 2006, в 23:57) писал(а):Во-первых, убери name из public, положи его в private. Возьми за правило - не делать public и protected данных. Потом будет проще

Спасибо. :)
(sdk @ Dec 3 2006, в 23:57) писал(а):public-наследование нужно для отношений типа IS-A, то есть, когда ты моделируешь нечто, что ЯВЛЯЕТСЯ тем, от чего ты наследуешься. Танк не ЯВЛЯЕТСЯ оружием - это одна из его составляющих

То есть, если бы я наследовал, например, не Tank, а ShotGun, TommyGun и т. д., то наследование должно было быть public (дробовик и автомат являются оружием, а не только состовляющей его частью)?
(sdk @ Dec 3 2006, в 23:57) писал(а):Пардон за сумбурность, у меня в голове это все на английском

Да ничего страшного, я, кажется, все понял. :)


(sdk @ Dec 3 2006, в 23:57) писал(а):string name() const { return m_name; }

Насколько я понял, метод возвращает содержание поля m_name типа string, а для чего const здесь? :)

Про Weapon *m_weapon я понял, но не понял как теперь достучаться до полей Weapon через Tank. :)

PS: у нас здесь ночь уже, так что может я не вижу элементарных вещей. Если это так, то прошу меня извинить. :)
Спасибо сказали:
Аватара пользователя
serzh-z
Бывший модератор
Сообщения: 8259
Статус: Маньяк
ОС: Arch, Fedora, Ubuntu

Re: Одиннаковые имена полей разных классов.

Сообщение serzh-z »

iAm писал(а):
03.12.2006 17:39
Насколько я понял, метод возвращает содержание поля m_name типа string, а для чего const здесь? :)

Про Weapon *m_weapon я понял, но не понял как теперь достучаться до полей Weapon через Tank. :)
1. чтобы указать, что get_name() не меняет данных класса, а лишь читает
2. Tank::getWeaponName() const { return m_weapon->getName(); }
Спасибо сказали:
iAm
Сообщения: 220
ОС: Gentoo

Re: Одиннаковые имена полей разных классов.

Сообщение iAm »

Ага, ясно. То есть все упирается к тому, что забирать данные нужно с помощью методов, а не напрямую.

Код: Выделить всё

class Unit{
    public:
        Unit(){ name = "UnitName"; }
        string GetName() const { return name; }
    private:
        string name;
};

class Weapon{
    public:
        Weapon(){ name = "WeaponName"; }
        string GetName() const { return name; }
    private:
        string name;
};

class Tank: public Unit{
    public:
        Tank(){ Weapon* my_weapon = new Weapon; }
        string GetWeaponName() const { return my_weapon->GetName(); }
    private:
        Weapon* my_weapon;
};

Tank* tanks = new Tank;
tanks->GetWeaponName();
Спасибо сказали:
sdk
Бывший модератор
Сообщения: 210

Re: Одиннаковые имена полей разных классов.

Сообщение sdk »

Да. Либо как вариант ты можешь возвращать указатель на Weapon из Tank. Например:

Код: Выделить всё

class Tank
{
   public
     Weapon* getWeapon() { return m_weapon; }
}

.....

Tank *t = new Tank();
cout << t->getWeapon()->getName() << endl;


Но тут нужно быть уверенным в том что ты делаешь. Например, код выше позволяет кому угодно модифицировать (или вообще удалить) возвращенный Weapon. То есть запросто можно будет сделать:

Код: Выделить всё

Tank *t = new Tank();
Weapon *w = t->getWeapon();
delete w;
// и теперь Tank остался без оружия и что хуже всего он об этом не подозревает


Поэтому, в данном случае лучше понаставить везде const-ов - тогда никто не сможет модифицировать возвращенный указатель. Например:

Код: Выделить всё

class Tank
{
   public:
     const Weapon* const getWeapon() const { return m_weapon; }
};

Или возвращать "const Weapon&" - тот же эффект.

Ну или предоставлять функции-обертки, как ты написал в предыдущем посте.

А вообще, возьми почитай того же Мейерса. Там про все это есть ;).
Серьезность - это способ сделать простые вещи сложными.
Если много знать - устанут глаза. Если много спать - то нет.
Нас никому не сбить с пути - нам пофигу куда идти.
:-)
Спасибо сказали:
v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Одиннаковые имена полей разных классов.

Сообщение v04bvs »

Код: Выделить всё

Tank *t = new Tank();
Weapon *w = t->getWeapon();
delete w;
// и теперь Tank остался без оружия и что хуже всего он об этом не подозревает

Пока у нас такие программисты, враг не пройдёт :)

Поэтому, в данном случае лучше понаставить везде const-ов - тогда никто не сможет модифицировать возвращенный указатель.

А я смогу :) По крайней мере в большинстве случаев.

Автор топика: почитай хорошую книжку, конечно тут много хорошего насоветуют, но фундаментальные знания надо в книжке приобретать.
Спасибо сказали:
sdk
Бывший модератор
Сообщения: 210

Re: Одиннаковые имена полей разных классов.

Сообщение sdk »

(v04bvs @ Dec 4 2006, в 01:50) писал(а):А я смогу smile.gif По крайней мере в большинстве случаев.

const_cast-ом? Ну это все равно шо гранату кинуть ;).
Серьезность - это способ сделать простые вещи сложными.
Если много знать - устанут глаза. Если много спать - то нет.
Нас никому не сбить с пути - нам пофигу куда идти.
:-)
Спасибо сказали:
iAm
Сообщения: 220
ОС: Gentoo

Re: Одиннаковые имена полей разных классов.

Сообщение iAm »

Спасибо всем. :)
Тему можно закрывать.
Спасибо сказали:
v04bvs
Сообщения: 636
ОС: Debian GNU/Linux

Re: Одиннаковые имена полей разных классов.

Сообщение v04bvs »

sdk писал(а):
04.12.2006 01:19
(v04bvs @ Dec 4 2006, в 01:50) писал(а):
А я смогу smile.gif По крайней мере в большинстве случаев.

const_cast-ом? Ну это все равно шо гранату кинуть ;).

Ну как сказать. Если данные изначально константные, то да, а если просто прокастовались к константным, то с большой вероятностью ничего плохого не будет. Хотя конечно нельзя так делать. Это я так, "никто" глаза резало :)
Спасибо сказали: