Вызов виртуальной функции перед реализацией - PullRequest
3 голосов
/ 26 октября 2011

Итак, у меня есть такая настройка

class Base
{
public:
  Base();
  virtual void parse() = 0;
};

Base::Base()
{
  parse();
} 

class Sub : public Base
{
public:
  Sub();
  void parse();
};

Sub::Sub() : Base() {}

void Sub::parse()
{
  // DO stuff
}

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

Я думаю, что было бы неплохо сделать parse () просто виртуальным, а не чисто виртуальным, но я бы хотел, чтобы пользователь переписал его.*

Ответы [ 4 ]

10 голосов
/ 26 октября 2011

Вызов виртуальных функций-членов в конструкторе (или деструкторе) никогда не приведёт вас к переопределенной версии этой функции в производном классе.

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

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

2 голосов
/ 26 октября 2011

Разрешение виртуальной функции не может найти функцию в классе который не был построен. (Формально: динамический тип объект - это конструктор или деструктор, который работает.) вам нужен какой-то пост-конструктор, который язык не делает поддержка.

Вы можете обойти это, используя фиктивный параметр, который вызывает функция, которая вам нужна в деструкторе, например ::1003

class Base
{
public:
    class PostConstructor
    {
        Base* owner;
        friend class Base;
    public:
        PostConstructor() : owner( NULL ) {}
        ~PostConstructor() { owner->parse(); }
    };
    Base( PostConstructor const& helper )
    {
        helper.owner = this;
    }
};

class Derived : public Base
{
public:
    Derived( PostConstructor const& helper = PostConstructor() )
        : Base( helper )
    {
    }
};

или если у класса есть аргументы:

class Base
{
    std::string name;
public:
    class PostConstructor
    {
        Base* owner;
        std::string arg;    //  for example...
        friend class Base;
    public:
        PostConstructor( std::string const& arg )  // implicit conversion!!
                : owner( NULL ), arg( arg ) {}
        ~PostConstructor() { owner->parse(); }
        operator std::string() const { return arg; }
    };
    Base( PostConstructor const& helper )
        : name( helper )
    {
        helper.owner = this;
    }
};

class Derived : public Base
{
public:
    Derived( PostConstructor const& helper )
        : Base( helper )
    {
    }
};

Это работает, потому что PostConstructor будет временным, разрушенным в конце полного выражения (когда Derived полностью построен).

В обоих случаях: производные классы должны взаимодействовать (но они не будут компилировать, если они этого не делают). Трюк также может потерпеть неудачу, если класс построен в середине более сложного выражения. Что-то как:

Derived(...).someFunction();

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

Тем не менее, несмотря на ограничения, я нашел это полезным в некоторых случаях.

2 голосов
/ 26 октября 2011

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

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

В общем случае для этого нет решения: вы не можете отправлять виртуальные функции в конструкторы или деструкторы.

0 голосов
/ 26 октября 2011

Абстрактные или чистые методы являются частным случаем виртуальных методов (Все абстрактные или чистые методы являются виртуальными).

Мой предыдущий ответ неверен, потому что я пропускаю конструктор.Конструкторы на C ++ не являются виртуальными, и не позволяет вызывать виртуальные (абстрактные и не абстрактные методы) внутри конструкторов .Если вы вызываете неабстрактный переопределенный «синтаксический анализ» из другого метода, который не является конструктором, все будет в порядке.

Проблема не в том, что метод его абстрактный, а в том, что он вызывается из конструктора.*

#include <conio>

class Base
{
public:
  // a constructor:
  Base();

  // "virtual and abstract" method:
  virtual void parse() = 0;

  // "virtual non abstract" method:
  virtual void hello();
};

// Error: you cannot call a virtual method from a constructor,
// wheter is abstract or not:
Base::Base()
{
  // error:
  parse();

  // error:
  hello();
} 

Base::hello()
{
  cout << "Hello World\n";
} 

class Sub : public Base
{
public:
  Sub();

  // forgot "virtual" here,
  // other languages use "override" instead, here:
  virtual void parse();
  // another "overriden" methods:
  virtual void parse();
};

// right: its important to call the base constructor,
// in this case:
Sub::Sub() : Base()
{
  // ...
}

void Sub::parse()
{
  // DO stuff
}

int main()
{
  Base *MyBaseObject = new Base();
  MyObject->parse();    

  Sub *MyObject = new Sub();
  MyObject->parse();

  return 0;
}

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

#include <conio>

class Base
{
public:
  // a constructor:
  Base();

  // a "postconstructor" or "pseudoconstructor"
  virtual void create();

  // "virtual and abstract" method:
  virtual void parse() = 0;

  // "virtual non abstract" method:
  virtual void hello();
};

// Error: you cannot call a virtual method from a constructor,
// wheter is abstract or not:
Base::Base()
{
  // no virtual methods called here,
  // wheter abstract or not
} 

// its not a real constructor, just a virtual method:
void Sub::create()
{
  // ...
}   
Base::hello()
{
  cout << "Hello World\n";
} 

class Sub : public Base
{
public:
  Sub();

  virtual void create();

  // forgot "virtual" here,
  // other languages use "override" instead, here:
  virtual void parse();
  // another "overriden" methods:
  virtual void parse();
};

// right: its important to call the base constructor,
// in this case:
Sub::Sub() : Base()
{
  // ...
}

// its not a real constructor, just a virtual method:
void Sub::create() : create()
{
  parse();
}

void Sub::parse()
{
  // DO stuff
}

int main()
{
  // this commented code, wont work
  /*
  Base *MyBaseObject = new Base();
  MyObject->create();    
  MyObject->parse();    
  */

  // calling "pseudo-constructor",
  // just after real constructor
  Sub *MyObject = new Sub(); MyObject->create();
    MyObject->parse();

  return 0;
}

Моя ошибка извините.

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