оператор = в подклассе - PullRequest
0 голосов
/ 09 июля 2010

Я не совсем понимаю, как решить следующую проблему (поиск как по SO, так и по Google не сильно помог).

Скажем, у нас есть класс, определяющий базовые операторы и простой вектор как только данныеи добавьте только дополнительные методы в унаследованные классы:

class Foo
{
    public:
        // this only copies the data
        Foo& operator=(const Foo& foo);

        // do something that computes a new Foo from *this
        Foo modifiedFoo();

    //..
    private:
        std::vector<int> data;
}

class Bar: public Foo
{
    public:
       void someNewMethod();
    //..
    // no new data
}

Наследование теперь гарантирует, что оператор = в случае bar1 = bar2 делает правильные вещи.Но с точки зрения данных, и Bar, и Foo в основном одинаковы, поэтому я хотел бы иметь возможность писать как

Foo foo;
Bar bar;

foo = bar;  // this ...
bar = foo;  // .. and this;

, так и более конкретно

bar = foo.modifiedFoo();

[Редактировать: И, кстати, это тоже не сработает ...

bar1 = bar2.modifiedFoo();

]

Я думал, что это будет так же просто, как добавить еще один Bar& operator=(const Foo & foo) в Bar, но почему-то это игнорируется (а мне все равно это не нравится, что если я получу все больше и больше классов?)

Так как же правильно поступить об этом ??

Спасибо !!И извините, если об этом уже спрашивали.

Ответы [ 3 ]

4 голосов
/ 09 июля 2010

Это известно как проблема среза:

foo = bar;

В основном объект bar используется так, как если бы он был только Foo.
Затем часть Foo бара копируется в объект foo (таким образом отсекая любые знания, что это был Bar).

Поскольку компилятор автоматически определяет оператор присваивания для класса.Следующее недопустимо:

bar = foo; 

Здесь компилятор видит, что bar имеет тип Bar, и ищет оператор присваивания.Он находит компилятор, сгенерированный в «Bar», и пытается применить его (тот, что в Foo, теперь скрыт).Сгенерированный компилятором оператор присваивания выглядит следующим образом:

Bar& operator=(Bar const& rhs)

Таким образом, приведенная выше строка не совпадает, и присваивание завершается неудачей.

1 голос
/ 10 июля 2010

Мартин рассказал вам, что пошло не так , вот что вы должны сделать вместо этого:

Bar bar;
Foo& foo = bar;  // this works now

Теперь foo является ссылкой к объекту, и вы можете иметь ссылки на базовый класс (и указатели, BTW), ссылающиеся на объекты производных классов.

Однако этот

bar = foo;

никогда не будет работать (по крайней мере, неявно) и не должен.Здесь вы пытаетесь назначить объект базового класса (или ссылку) производному классу.
Однако, хотя производный класс всегда может заменить объект базового класса, обратное неверно.(Вы не можете просто бездумно использовать любое транспортное средство , когда вам нужна лодка , потому что не все транспортные средства являются лодками,транспортное средство, которое вы используете, может быть автомобилем , и вы утонете.)

Это не означает, что вы не можете заставить его работать.если вы знаете, как создать объект производного класса из объекта базового класса, вы можете написать для этого функцию.Просто компилятор не знает, как сделать автомобиль из автомобиля .

Как показало solipist , такая функция преобразования может быть конструктором.Однако я хотел бы сделать конструкторы преобразования явными:

class Bar: public Foo
{
    public:
        explicit Bar(const Foo&);
    //
}

Ключевое слово explicit гарантирует, что компилятор никогда не попытается вызвать этот конструктор, если вы не скажете так.Если вы удалите его, то этот код скомпилирует:

//void f(Foo); // you meant to call this, but forgot to include its header
void f(Bar); // you didn't even know this was included

Foo foo;
f(foo);  // won't compile with the above Bar class

, и компилятор сгенерирует этот код в молчании: f(Bar(foo));
Хотя это поначалу кажется удобным, я думаю, что рано или поздноукушенный каждым неявным преобразованием, я позволил проникнуть в мой код и позже должен был удалить их.Так много лет назад я поклялся никогда не допускать их.
Обратите внимание, что даже с конструктором преобразования explicit вы все равно можете вызывать f(Bar) с объектом Foo - вы просто должны сказать это явно:

f(Bar(foo)); // compiles fine even with the explicit conversion constructor
0 голосов
/ 10 июля 2010

Если вы хотите иметь возможность неявно приводить Foo s к Bar s, создайте неявный конструктор.

class Bar: public Foo
{
    public:
        Bar(const Foo&);
    //
}

Теперь, когда вы пишете 'bar = foo', Foo неявно преобразуется во временную Bar, поэтому оператор присваивания Bar может быть успешно использован.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...