2015-10-25 4 views
1

У меня есть base_class, у которого нет конструктора по умолчанию, и я бы хотел определить его векторную версию (так называемый derived_class). Я знаю, что я должен инициализировать конструктор base_class в моем конструкторе derived_class, но код, ниже которого попытки инициализировать вектор размером dim и base_class(0,1) в каждой строке не может быть скомпилирован (он жалуется ошибка: конструктор для 'производного_класса' должен явно инициализировать базу class 'base_class', который не имеет конструктора по умолчанию), если я не добавлю конструктор по умолчанию (прокомментированная строка) к base_class. Я пропускаю или неправильно понимаю что-то? Есть ли способ заставить его работать без определения конструктора по умолчанию base_class?Как инициализировать вектор конструкторов базового класса?

#include <iostream> 
#include <vector> 

class base_class 
{ 
    public: 
     //base_class(){}; 
     base_class(double a, double b){a_=a; b_=b;} 
    private: 
     double a_, b_; 
}; 

class derived_class : public base_class 
{ 
    public: 
     derived_class(int dim): vector_object(dim, base_class(0,1)){}; 
     std::vector<base_class> print_vector_object() {return vector_object;} 
    private: 
     std::vector<base_class> vector_object; 
}; 


int main() 
{ 
    int dim = 3; 
    derived_class abc(3); 
    std::cout << abc.print_vector_object().size() << std::endl; 
    return 0; 
} 

UPDATE: Я понимаю, что я могу полностью избежать наследования здесь, в этом простом случае, но, пожалуйста, предположим, что мне нужно наследование для реальной практики, спасибо.

UPDATE2: Как намекают @vishal, если конструктор derived_class записывается как

derived_class(int dim) : base_class(0,0), vector_object(dim, base_class(0,1)) {}; 

код может передать компилятор. Но я до сих пор не понимаю, почему я должен инициализировать таким образом ...

+0

Я не уверен, что «векторная версия» - это разумная вещь, которую нужно хотеть. –

+0

@ KerrekSB, я знаю, и, как указано в ответах ниже, моя жизнь была бы легче, если бы не так.Но, пожалуйста, предположите, что я хочу определить векторизованный производный класс и сосредоточиться на причине, почему мой пример не работает. –

ответ

6

Ну, так как вы не хотите, чтобы создать конструктор по умолчанию в базовом классе, вы можете call base constructor by:

derived_class(double a, double b, int dim) : base_class(a,b), vector_object(dim, base_class(0,1)) {}; 
+0

Уважаемый @vishal, спасибо за ваш быстрый ответ! Но разве это не избыточно, что каждый раз, когда я создаю объект 'derive_class', я должен указать три аргумента:' a', 'b' и' dim', два из которых бесполезны? На самом деле я пробовал несколько иной подход: 'производный_класс (int dim): base_class (0,0), vector_object (dim, base_class (0,1)) {};' Но при выполнении этого результат выглядит странно ... –

+2

@LeoFang Используйте параметры по умолчанию, а затем для 'a' и' b' (и поместите их в конец списка параметров), например 'производный_класс (int dim, double a = 0, double b = 1): ...' This также следует задуматься над тем, насколько ваш дизайн звучит или нет. – vsoftco

+3

Хотя этот ответ является синтаксически правильным, последующее обсуждение полностью отключается от рельсов. Посылка вопроса полностью нарушена. –

1

Ваш vector_object является отвлекающим маневром и не имеет ничего общего с этой проблемой, как вы можете проверить с помощью следующего фрагмента кода, который также заставит компилятор жалуется отсутствующий конструктор по умолчанию: base_class

class derived_class : public base_class 
{ 
    public: 
     derived_class(int dim) {} // error, tries to use `base_class` default constructor, 
           // but there is none... 
}; 

вы должен либо предоставить конструктор по умолчанию, либо использовать нестандартный. В каждом случае derived_class существует под-объект base_class, и подобъект должен быть каким-то образом создан.

Одним из возможных источников недопонимания является тот факт, что объекты в иерархии наследования: , инициализированный сверху донизу (базовый для производного). Это может показаться нелогичным с первого взгляда, но к тому моменту, когда запускается конструктор derived_class, под-объект base_class должен уже существовать. Если компилятор не может этого предусмотреть, вы получите сообщение об ошибке.

Таким образом, даже несмотря на то, derived_class конструктор определяет , как создаетсяbase_class суб-объект, фактическое создание из подъобекта происходит перед созданием derived_class части.

Вы спрашиваете следующее:

shouldn't the base_class object be initialized in my code?

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

Иногда это помогает рассматривать наследование особую форму композиции (действительно, есть некоторые поразительные сходства). Следующий фрагмент коды страдает от тех же проблем, как один в вашей проводке:

class base_class 
{ 
    public: 
     //base_class(){}; 
     base_class(double a, double b){a_=a; b_=b;} 
    private: 
     double a_, b_; 
}; 

class derived_class // <--- no inheritance 
{ 
    public: 
     base_class my_base; // <--- member variable 

     derived_class(int dim) {}; 
}; 

Вы все еще думаете, что derived_classbase_class инициализирует объект?


Другим возможным источником недопонимания является вышеупомянутая красная сельдь. Вы говорите:

I just don't understand why I cannot initialize it in a vector (...).

Поскольку переменная вектор член не имеет ничего общего с base_class подъобекта, или с общей переменной base_class члена в моем другом примере. Нет никакой магической связи между переменной-членом вектора и чем-либо еще.

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

+-------------------+ 
| +---------------+ | 
| |  double | | <--- `base_class` sub-object 
| |  double | |   
| +---------------+ | 
+-------------------+    +--------+--------+--------+.... 
|  std::vector ---------------> | double | double | double | 
+-------------------+    | double | double | double | 
            +--------+--------+--------+.... 
             ^
             | 
             | 
            a lot of other 
           `base_class` objects 

В base_class объекты, управляемые вектора совершенно не связаны к base_class суб-объект, обязана своим существованием классовому наследованию.

(Диаграмма чуть более упрощенно, поскольку std::vector обычно также хранит некоторые внутренние данные бухгалтерского учета, но это не имеет отношения к этой дискуссии.)


Однако, ваш код не в любом случае сделать убедительный случай для наследования. Так почему вы наследуете в первую очередь? Вы можете также сделать так:

#include <iostream> 
#include <vector> 

class base_class 
{ 
    public: 
     //base_class(){}; 
     base_class(double a, double b){a_=a; b_=b;} 
    private: 
     double a_, b_; 
}; 

class derived_class // <--- no more inheritance (and thus wrong class name) 
{ 
    public: 
     derived_class(int dim) : vector_object(dim, base_class(0,1)){}; 
     std::vector<base_class> print_vector_object() {return vector_object;} 
    private: 
     std::vector<base_class> vector_object; 
}; 


int main() 
{ 
    int dim = 3; 
    derived_class abc(3); 
    std::cout << abc.print_vector_object().size() << std::endl; 
    return 0; 
} 
+0

Дорогой христианин, спасибо за ваш ответ. Два ответа: 1. Я понимаю, что, поскольку нет конструктора по умолчанию, мне нужно вызвать нестандартный, и я подумал, что способ инициализации конструктора «производного класса» достаточно, но это не так, и я не понять почему. 2. Мой пример упрощен, так что, как вы указали, в этом случае нет необходимости в наследовании. –

+0

@LeoFang: Что именно вы подразумеваете под «инициализацией конструктора»? Вы * вызываете * конструкторы и вы * инициализируете * объекты. –

+0

Я имел в виду, когда я вызываю конструктор 'производного_класса', не должен ли инициализироваться объект' base_class' в моем коде? –

0

Вы уверены, что derived_class -объектом является base_class -объектом и держит vectorbase_class из -Объектов?

Я предполагаю, что вы на самом деле хотите только vectorbase_class -объектов. Если это так, не выводите derived_class из base_class. На самом деле это то, откуда приходит ваше сообщение компилятора. Он сообщает вам, что вам нужно будет инициализировать аспект base_class вашего объекта derived_class.

Так вот мое решение для вас (остерегайтесь, код не тестировался, и, вероятно, содержат некоторые ошибки):

#include <iostream> 
#include <vector> 

class base_class 
{ 
    public: 
     // c++11 allows you to explicitly delete the default constructor. 
     base_class() = delete; 
     base_class(double a, double b): 
      a_{a}, b_{b} // <- prefer initializers over constructor body 
     {}; 
    private: 
     double a_; 
     double b_; 
}; 

class derived_class // <- this name is wrong now of course; change it. 
{ 
    public: 
     derived_class(int dim): 
      // better initialize doubles with float syntax, not int. 
      vector_object{dim, base_class{0.0, 1.0}} 
     {}; 

     // Note: this will copy the whole vector on return. 
     // are you sure, that is what you really want? 
     auto print_vector_object() -> std::vector<base_class> { 
      return vector_object; 
     }; 
    private: 
     std::vector<base_class> vector_object; 
}; 


int main(int, char**) 
{ 
    // int dim = 3; <- this is useless, you never use dim. 
    auto abc = derived_class{3}; 
    std::cout << abc.print_vector_object().size() << std::endl; 
    return 0; 
} 

Примечание: мой код предназначается, чтобы быть C++ 11.

+0

* «стандарт требует этих параметров» * - Это неверно. 'int main()' является совершенно законным C++. Как исключает «return 0;». –

+0

@ChristianHackl Я просто посмотрел в стандарт. Вы правы, я отредактирую свой ответ. – cdonat

0

Я переписал ваш класс немного

#include <iostream> 
#include <vector> 

class base_class { 
private: 
    double a_, b_; 

public: 
    //base_class(){}; 
    base_class(double a, double b) : a_(a), b_(b) {} 
    virtual ~base_class(); 
}; 

class derived_class : public base_class { 
private: 
    std::vector<base_class> vector_object; 

public: 
    explicit derived_class(int dim) : base_class(0, 1) { 
     vector_object.push_back(static_cast<base_class>(*this)); 
    } 

    ~derived_class(); 

    std::vector<base_class> get_vector_object() const { return vector_object; } 
}; 

int main() { 

    int dim = 3; 
    derived_class abc(3); 
    std::cout << abc.get_vector_object().size() << std::endl; 
    return 0; 
} 

Это создает и компилирует правильно.Было внесено несколько изменений: я использовал список инициализаторов элементов конструкторов, где это применимо, я добавил виртуальный деструктор в ваш базовый класс; каждый раз, когда вы наследуете базовый класс, должен иметь виртуальный деструктор, я переместил все частные переменные-члены в начало класса, а не внизу, я префикс конструктора производного класса с явным ключевым словом, так как у вас есть только один параметр, переданный в него , Я изменил конструктор output_class, чтобы использовать конструктор base_class в списке инициализации его члена, и я заполнил его вектор-переменную-член внутри конструктора, используя метод push_back(), и статически лидировал разыменованный этот указатель на тип base_class, и я также изменил метод print_vector_object на просто get_vector_object, поскольку он ничего не печатает и возвращает вектор-член из класса. Я также объявлял этот метод как const, чтобы он не менял класс, поскольку вы только извлекаете его. Единственный вопрос, который у меня есть, - это то, что делает параметр int в производном_классе? Он не используется ни в одном месте, и у вас нет переменной-члена для его сохранения.

В этом разделе вашего производного класса здесь

public: derived_class(int dim): vector_object(dim, base_class(0,1)){};

где вы объявляете ваш конструктор не делает никакого смысла для меня.

Похоже, что вы пытаетесь использовать список инициализации членов, чтобы заполнить вашу переменную-член вектора, передав в переданный параметр вашего производного класса и вызвав конструктор в ваш базовый класс со значениями {0,1}. Это не будет работать так, как вы ожидали. Сначала вам нужно создать объект Base Class, который является под-объектом для этого производного класса, чем в области функций вашего конструктора, который вы можете заполнить своим вектором-членом.

У вас возникнет проблема, потому что у вас нет конструктора по умолчанию, который доступен для создания и создания производного типа. Поскольку из того, что я могу собрать на том, что вы показали и что кажется, что вы пытаетесь сделать, кажется, что вы хотите, чтобы base_class инициализировался до {0,1}, когда вызывается производный конструктор; если это так, вы можете просто вызвать свой реализованный конструктор для base_class с {0,1}, переданный в его конструкторе, в то время как ваш производный тип пытается построить. Это позволит правильно сформировать производный класс, поскольку base_class был сконструирован. Теперь, когда вы можете начать создание своего производного типа, похоже, что вы хотите сохранить сконструированный base_class в переменную-член вектора производных типов. Вот почему я делаю это внутри конструктора, а не в списке инициализации, и я push_back - экземпляр производного типа, который статически возвращается к базовому типу с использованием разыменованного этого указателя. Также, когда я устанавливаю точку прерывания в основной функции, где вы пытаетесь напечатать свой размер, и я смотрю на переменную-член производного класса, она заполняется 1 объектом, который имеет значения a = 0 и b = 1. Чтобы иметь возможность печатать вам нужно добавить public get методы для возврата каждой переменной. Кроме того, если параметр int в производном классе не нужен, вы можете удалить его вместе с явным ключевым словом. Кроме того, можно реализовать второй конструктор производного типа, который принимает в двух дублей так же, как основание делает и передать эти параметры из конструктора derived_class конструктору base_class, как это:

public: 
    derived_class(double a, double b) : base_class(a, b) { 
     vector_object.push_back(static_cast<base_class>(*this)); 
    } 

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

class Base { 
private: 
    double x_, y_, z_; 

public: 
    Base(double x, double y, double z); 
    virtual ~Base(); 
}; 

Base::Base() : 
x_(x), y_(x), z_(z) { 
} 

Base::~Base() { 
} 

class DerivedA : public Base { 
private: 
    int value_; 

public: 
    explicit DerivedA(int value); 
    ~DerivedA(); 
}; 

// In This Case A Must Set The Values In The Constructor Itself: I'll Just 
// Set Them To (0, 0, 0) To Keep It Simple 
DerivedA::DerivedA(int value) : 
Base(0, 0, 0), 
value_(value) { 
} 

DerivedA::~DerivedA() { 
} 

class DerivedB : public Base { 
private: 
    int value_; 

public: 
    DerivedB(int value, double x, double y, double z); 
    ~DerivedB(); 
}; 

// In This Case B Doesn't Have To Know What Is Needed To Construct Base 
// Since Its Constructor Expects Values From Its Caller And They Can 
// Be Passed Off To The Base Class Constructor 
DerivedB::DerivedB(int value, double x, double y, double z) : 
Base(x, y, z), 
value_(value) { 
} 

DerivedB::~DerivedB() { 
}  

Дайте мне знать, если это поможет вам!

Смежные вопросы