Как инициализировать переменную-член с типом суперкласса в зависимости от аргумента конструктора? - PullRequest
0 голосов
/ 30 мая 2018

После поисков часов я попал сюда.У меня есть класс контейнера с указателем на базовый класс в качестве переменной-члена.Это должно относиться либо к Spec1, либо к другим унаследованным классам Base, которые я здесь опускаю.Тип должен определяться аргументом в конструкторе (например, string, enum, int и т. Д.).

Я много читал о динамическом распределении памяти и о том, почему его следует избегать, когда это возможно.Можно ли здесь избежать?Разве обычный конструктор не разрушен после конструктора?Или идея дизайна совершенно неверна?Я с Явы :( Спасибо заранее.

class Base{
  public:
    virtual ~Base(){}; // required?
    virtual void doSomething() = 0;
};

class Spec1 : public Base {
     public:
       Spec1(){};
       Spec1(int i){
         // whatever
       }
       void doSomething(){
          std::printf("hello world");
       }
};

class Container{
   public:
     Container(String type_message){
      if (type_message.compare("We need Spec1")){
          m_type = new Spec1(1);
      } // add more ifs for other types (Spec2, Spec3 etc.)
     }
     void doSomethingWithSpec(){
        m_type->doSomething();
     }
   private:
      Base* m_type;
 };

int main (int argc, char **argv){
    Container a ("We need Spec1");
    a.doSomething();
}

Ответы [ 2 ]

0 голосов
/ 30 мая 2018

Требование Container знать обо всех возможных производных классах Base не похоже на хороший дизайн.Вот для чего предназначены заводские функции.

Container должен хранить этот объект как std::unique_ptr, чтобы избежать утечек памяти и ручного управления памятью.

struct Base {
    virtual ~Base() = default;
    virtual void doSomething() = 0;
};

struct Spec1 : Base {
    void doSomething() override {
        std::printf("%s\n", __PRETTY_FUNCTION__);
    }
};

// Factory function.
std::unique_ptr<Base> createBase(std::string const& type) {
    if(type == "Spec1")
        return std::unique_ptr<Base>(new Spec1);
    throw std::runtime_error("Unknown type " + type);
}

class Container {
    std::unique_ptr<Base> m_type;
public:
    Container(std::string const& type)
        : m_type(createBase(type))
    {}

    void doSomething(){
        m_type->doSomething();
    }
};

int main() {
    Container a ("Spec1");
    a.doSomething();
}
0 голосов
/ 30 мая 2018

Конструктор работает как любая другая функция.Локальные переменные размещаются в стеке и выходят из области видимости всякий раз, когда функция завершает свою работу, тогда как динамическое выделение памяти выполняется в стеке и должно быть явно освобождено (в C ++, а не в Java) до завершения функции.

Однако вваш случай

m_type = new Spec1(1);

new Spec1(1) динамически размещается в куче и не будет уничтожен при завершении кода конструктора.Ссылка хранится в переменной-члене класса.Таким образом, до тех пор, пока экземпляр класса Container находится в области видимости, можно использовать память, выделенную для Spec1(1).

Для сравнения просто рассмотрим другой сценарий.

Container(String type_message){
      Base* m_type;
      if (type_message.compare("We need Spec1")){
          m_type = new Spec1(1);
      } // add more ifs for other types (Spec2, Spec3 etc.)
     }

Здесь каккак только конструктор завершит работу, m_type выйдет из области видимости, но new Spec1(1) останется в куче из-за утечки памяти.

...