2016-11-17 4 views
0

Предположим, у меня есть класс с именем Foo, который не должен быть инициализирован. Он должен быть унаследован для использования. Это делает Foo a абстрактным классом без чистых виртуальных функций. я упростил ради примера:Как сделать абстрактного члена класса?

class Foo : Qwe 
{ 
protected: 
    Bar& bar; 
    int baz; 
    Foo(int baz_) : baz(baz_) {} 
public: 
    virtual void function_that_uses_bar() const override 
    { 
     // code that uses bar here 
    } 
} 

Пример того, как Foo используется:

class Kappa : public Foo 
{ 
public: 
    Kappa() : Foo(10) 
    { 
     bar = Asd(); // Asd is a Bar 
    } 
} 

Поэтому в основном только классы, полученные из Foo знать, что должно bar быть установлен.

Ошибка, которую я получаю: Error C2530 'Foo::bar': references must be initialized, которая ссылается на конструктор Foo. Как это исправить?

+4

'Bar & bar' является ссылкой, поэтому он должен быть инициализирован в списке инициализации конструктора непосредственно перед' baz'. – Galik

+2

Конструктор 'Foo' должен инициализировать' bar'. Обычно это делается путем передачи 'Bar &' конструктору. –

+1

У вас есть ошибка дизайна. Если только дети Foo знают, как установить 'bar', чем' bar' не принадлежит Foo. – SergeyA

ответ

1

Прежде чем что-либо еще, так как вы находитесь на C++, нет абстрактного класса без чистых виртуальных функций. Это просто не вещь.

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

Bar myBar; 
Bar& bar; // Error 

bar = myBar; // don't do what you think 

Здесь, на линии, где я комментировал «Error», это потому, что вы не можете отложить инициализацию ссылки. Ссылка похожа на псевдоним на другую переменную. Ссылка должна быть инициализирована значением.

Строка с комментарием «не делай, что думаешь», потому что она не будет перепроверять ссылку. Он вызывается функцией Bar::operator=(const Bar&). Это сделает bar значение myBar.

Хорошо, с объяснением, будет легче понять, что происходит в вашем классе.

В вашем конструкторе Foo скрыты все происходящее.

В самом деле, он будет инициализировать Foo::baz со значением, попавшим в конструкторе, но он также будет инициализировать другие переменные ваш класс, немного напоминает, что:

//    implicit --v---v 
Foo(int baz_) : baz(baz_), bar() {} 

Смотрите эту проблему? Вы инициализируете ссылку iwth no variable для ссылки. Вам нужно передать переменную, на которую нужно привязать.

Вторая проблема, вы делаете что-то здесь не так:

Kappa() : Foo(10) 
{ 
    bar = Asd(); // Asd is a Bar 
} 

Вы имели в виду, что Asd тип, который расширяет Bar? Если это так, вы должны изменить экземпляр bar на то же значение пустого Asd. Да, он скопирует содержимое пустого Asd на ваш bar. Это семантика значений. Определенные пользователем типы ведут себя так же, как основы.


Хорошо, так что теперь, как исправить этот код?

Предполагаете, что вы хотите создать новый экземпляр базового класса и привязать ссылку к родительскому объекту этому новому экземпляру. Если да, то вам нужна переменная, которая может жить мимо рамки конструктора:

Kappa() : Foo(10) 
{ 
    Asd myAsd; 
    bar = /* somehow bind bar to myAsd */ 

} // myAsd destroyed here! 

Как вы можете видеть, вы должны создать переменную, которая должна жить мимо }. Идеал был бы переменной, которая живет достаточно долго, чтобы остаться в живых, пока Kappa останется в живых. Введите std::unique_ptr!

Вместо ссылки, не требующей права собственности, используйте std::unique_ptr. Это класс, который представляет переменную, которая выделяется в свободном хранилище, иначе. переменная, выделенная кучей. Он будет вести себя аналогично объекту в java или C# или < вставить имя управляемого языка>. Обычно используется std::unique_ptr наряду с std::make_unique для распределения.

Вы можете использовать его так: