абстрактные классы С++, использующие чистые виртуальные функции в нечистых виртуальных функциях

Я хочу создать абстрактный класс с чистой виртуальной функцией, которая вызывается НЕ чисто виртуальным конструктором. Ниже мой файл class.hpp:

#ifndef __CLASS_HPP__
#define __CLASS_HPP__

#include <iostream>

class Parent {
 public:
  Parent(){
    helloWorld(); // forced to say hello when constructor called                    
  };
  virtual void helloWorld() = 0; // no standard hello...                            
};

class Child : public Parent {
 public:
  void helloWorld(){ // childs implementation of helloWorld                         
    std::cout << "Hello, World!\n";
  };
};

#endif

В этом примере у меня есть родительский класс с чистой виртуальной функцией helloWorld(). Я хочу, чтобы каждый производный класс говорил "привет" при вызове конструктора; следовательно, почему helloWorld() находится в конструкторе родительского класса. Тем не менее, я хочу, чтобы каждый производный класс был ПРИНУДИТЕЛЬНО выбирать, как он будет говорить «привет», а не иметь метод по умолчанию. Это возможно? Если я попытаюсь скомпилировать это с помощью g++, я получу ошибку, что конструктор вызывает чистую виртуальную функцию. Мой main.cpp это:

#include "class.hpp"

int main(){
  Child c;
  return 0;
}

Я компилирую с использованием g++ main.cpp -o main.out, и в результате возникает ошибка:

In file included from main.cpp:1:0:
class.hpp: In constructor ‘Parent::Parent()’:  
class.hpp:9:16: warning: pure virtual ‘virtual void Parent::helloWorld()’ called from constructor [enabled by default]

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

НОВЫЙ ВОПРОС

DyP обратил мое внимание на то, что конструктор не использует никаких переопределенных функций, поэтому то, что я хочу сделать, невозможно в том виде, в котором я его настроил. Тем не менее, я все еще хотел бы заставить любой производный конструктор вызывать функцию helloWorld(), есть ли способ сделать это?


person nick    schedule 11.09.2013    source источник
comment
Вызов виртуальной функции из ctor (того же объекта) опасен, потому что он не вызывает переопределения из производных классов. В этом случае он пытается вызвать Parent::helloWorld, что является чистым, отсюда и предупреждение (чистая виртуальная функция все еще может иметь реализацию). Вы можете передать строку из производного класса в ctor базового класса через параметр и выполнить вывод в базовом классе. Также возможно: 2-ступенчатая инициализация.   -  person dyp    schedule 11.09.2013
comment
Кажется странным, что базовый класс зависит от производного класса. Можете ли вы уточнить проблему, которую вы пытаетесь решить? В зависимости от деталей могут быть доступны и другие варианты.   -  person Ben S.    schedule 11.09.2013
comment
В моей реальной проблеме у меня есть класс, который содержит данные для моделирования. Однако в симуляции есть множество особенностей, которые зависят от предпочтений пользователя. У меня есть базовый класс как абстрактный класс с конструктором, который считывает много данных и сохраняет их, но оставляет несколько абстрактных функций (это те, которые зависят от предпочтений uesr). Поэтому я хочу, чтобы любой пользователь был вынужден читать данные таким же образом, но также был вынужден реализовывать эти абстрактные функции.   -  person nick    schedule 11.09.2013
comment
Имена, содержащие два последовательных символа подчеркивания (__CLASS_HPP__), и имена, начинающиеся с символа подчеркивания, за которым следует заглавная буква, зарезервированы для реализации. Не используйте их.   -  person Pete Becker    schedule 11.09.2013
comment
@PeteBecker, что ты имеешь в виду?   -  person nick    schedule 11.09.2013
comment
Не используйте __CLASS_HPP__ в качестве защиты включения в свой код. Он имеет два последовательных знака подчеркивания (в начале и в конце), поэтому он зарезервирован для реализации.   -  person Pete Becker    schedule 11.09.2013
comment
@PeteBecker Итак, я могу использовать это для защиты class.cpp? Что я использую в качестве защиты для файла .hpp?   -  person nick    schedule 11.09.2013
comment
Нет, вы не можете использовать __CLASS_HPP__ где-либо в своем коде. Это зарезервировано для реализации.   -  person Pete Becker    schedule 11.09.2013


Ответы (2)


Почему бы вам просто не добавить его в конструктор каждого дочернего класса?

Если вы хотите не записывать его каждый раз в конструкторе (или даже пропускать или наследовать его), вы можете использовать CRTP:

class Parent {
 public:
  Parent(){};
  virtual void helloWorld() = 0; // no standard hello...                            
};

template <typename Par>
class ParentCRTP: public Parent {
 public:
  ParentCRTP(){
    Par::doHelloWorld();
  };
  virtual void helloWorld(){
    Par::doHelloWorld();
  }
};

class Child : public ParentCRTP<Child> {
 public:
  static void doHelloWorld(){ // childs implementation of helloWorld                         
    std::cout << "Hello, World!\n";
  };
};

Этот подход не даст вам указатель на дочерний класс в методе приветствия вашего ребенка - в этот момент экземпляр класса является только экземпляром Parent, не может быть получен действительный указатель Child. Чтобы принудительно выполнить метод Child после построения, вы можете использовать только двухэтапную инициализацию: сначала вы создаете экземпляр класса с помощью конструктора, а затем инициализируете его с помощью отдельного метода.

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

person j_kubik    schedule 11.09.2013

То, что вы делаете, незаконно.

Чтобы определить абстрактный класс в C++, ваш класс должен иметь хотя бы одну чисто виртуальную функцию. В твоем случае

virtual void helloWorld() = 0;

в данном случае вы правы.

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

So,

Parent(){
helloWorld(); // forced to say hello when constructor called                    
};

это незаконно.

Если вы хотите, вы можете реализовать свою чистую виртуальную функцию в производном классе, а затем вызвать helloWorld() из конструктора производного класса.

person ANjaNA    schedule 11.09.2013
comment
Это было бы не то, чего я хотел. Я хочу, чтобы каждый дочерний класс был вынужден вызывать helloWorld(). Код, который я пишу, будет использоваться другими, и я хочу, чтобы они были вынуждены вызывать эту функцию. - person nick; 11.09.2013
comment
Я понял. Вот почему вы вызываете helloWorld() внутри конструктора родительского класса. Но дело в том, что если вы хотите определить абстрактный класс, он должен содержать хотя бы одну чистую виртуальную функцию. В вашем случае это «helloWorld()», и он не имеет реализации на уровне родительского класса. - person ANjaNA; 12.09.2013