LINUX.ORG.RU

Виртуальные функции


0

0

Я пишу базовый класс в котором есть только пять виртуальных функций (ну он типа служит интерфкйсом) далее в томже файле производные от него классы в который эти функции реализованы. Объекты я естественно создаю только производных классом, но при компиляции при помощи g++ получаю такую хню на этапе компоновки: undefined reference to `vtable Basic_Class ... Что я не так делаю???

anonymous

телепаты вымерли.

anonymous
()

Забываешь сделать что-то, чьим результатом являлось бы появление vtable в обьектном файле от сходника в котором находится Basic_class. Без этих самых исходников подробнее сказать не могу.

naryl ★★★★★
()
Ответ на: комментарий от Uncle_Theodore

Сорри форматирование забыл

Код такой: basic.h:

class Basic { public: virtual float prob(float, float); virtual float angle(float); virtual float en(float); };

class Mech1 : public Basic { public: float prob(float, float); float angle(float); float en(float); };

basic.cc: float Mech1::prob(float x, float y) { .... }

float Mech1::angle(float x) { .... }

float Mech1::en(float x) { .... }

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

anonymous
()
Ответ на: комментарий от anonymous

Странные сообщения от компилятора...

Сходу вижу две ошибки. Во-первых, абстрактный метод определяется с " = 0;" на конце. Например, virtual float prob(float, float) = 0;

Во-вторых, в субклассах методы тоже нужно делать виртуальными.

naryl ★★★★★
()
Ответ на: комментарий от naryl

> Во-первых, абстрактный метод определяется с " = 0;" на конце. 

Так определяется _чисто_ виртуальный метод

> Во-вторых, в субклассах методы тоже нужно делать виртуальными.

Нет, ни в коем случае не нужно.


Автору. У тебя сейчас так?


class Basic{
public:

  virtual float prob(float,float);
  virtual float angle(float);
  virtual float en(float);
};

class Mech1: public Basic{
public:
  
  float prob(float,float);
  float angle(float);
  float en(float);
};

float Mech1::prob(float x, float y)
{
  cout << "Mech1 prob\n";
};

float Mech1::angle(float x)
{
  cout << "Mech1 angle";
};

float Mech1::en(float)
{
  cout << "Mech1 en\n";
}

А ты сделай в первом классе вот так

class Basic{
public:

  virtual float prob(float,float){};
  virtual float angle(float){};
  virtual float en(float){};
};

Теперь работает?

Uncle_Theodore ★★
()
Ответ на: комментарий от Uncle_Theodore

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

anonymous
()
Ответ на: комментарий от anonymous

Всё просто. "Интерфейс" превратился в конкретный класс с пустыми реализациями всех методов. Попробуй создать его экземпляр.

naryl ★★★★★
()
Ответ на: комментарий от naryl

Да. Теперь вариант номер два, который ты предлагал.

Автор -- попробуй поменять в Basic методы на чисто виртуальные. Вот так

class Basic{
public:

  virtual float prob(float,float)=0;
  virtual float angle(float)=0;
  virtual float en(float)=0;
};

Все равно работает, да? Только теперь создать объект класса Basic 
невозможно.

Проблема была в том, что оригинальный код был ни тем, ни другим. А
что-то должно быть. Методы были ни чисто виртуальными, ни конкретными.

Uncle_Theodore ★★
()
Ответ на: комментарий от naryl

Ага и вызываются эти пустые реализацииб а не реализации производных классов, так что не прокатило :(

anonymous
()
Ответ на: комментарий от anonymous

Не может этого быть. Я же просил проверить... Вот у меня код для 
тестирования. Что он у тебя выдает?

#include<iostream>

using namespace std;

class Basic{
public:

  virtual float prob(float,float){};
  virtual float angle(float){};
  virtual float en(float){};
};



class Mech1: public Basic{
public:
  
  float prob(float,float);
  float angle(float);
  float en(float);
};

float Mech1::prob(float x, float y)
{
  cout << "Mech1 prob\n";
};

float Mech1::angle(float x)
{
  cout << "Mech1 angle\n";
};

float Mech1::en(float)
{
  cout << "Mech1 en\n";
};

int main()
{

  Mech1 mech;
  mech.prob(2.1,3.1);
  mech.angle(2.1);
  mech.en(2.0);

  return 0;
}

$ ./virvir 
Mech1 prob
Mech1 angle
Mech1 en

Uncle_Theodore ★★
()
Ответ на: комментарий от anonymous

_Попробуй_ в субклассах сделать методы виртуальными, а в базовом - чисто-виртуальными.

naryl ★★★★★
()
Ответ на: комментарий от Uncle_Theodore

Неа тут другая проблема: Если сделать так: std::vector<Basic> vec; Mech1 obj; vec.push_back(obj);

вылетает при компиляции, типа ... error: cannot allocate an object of abstract type... Обидно ....

anonymous
()
Ответ на: комментарий от anonymous

> вылетает при компиляции, типа ... error: cannot allocate an object of abstract type... Обидно ....

Так и невозможно, конечно. Абстрактный класс создается, чтобы никогда его объекты не создавать. Можно создавать указатели на объекты абстрактного класса, однако.

Uncle_Theodore ★★
()
Ответ на: комментарий от Uncle_Theodore

Ладно, похоже ты последний раз работал с C++ не 3 года назад, как я :) Доверю решение тебе.

naryl ★★★★★
()
Ответ на: комментарий от Uncle_Theodore

А когда обращаешся к методу из базового класса то не работает: Mech1 mech; Basic b = mech; b.en(0); последнее возвращает что-то в себе никак не связанное с кодом реализации и тем более ничего не печатает...

anonymous
()
Ответ на: комментарий от anonymous

> А когда обращаешся к методу из базового класса то не работает: 
Mech1 mech; Basic b = mech; b.en(0); последнее возвращает что-то в
себе никак не связанное с кодом реализации и тем более ничего не 
печатает...

Оно ничего не должно делать. Метод же пустой. Ты определись, должен ли 
Basic быть чисто абстрактным классом или все-таки ты хочешь иногда 
создавать объекты этого класса. Наследование будет работать в обоих 
случаях. Вот, например

class Basic{
public:

  virtual float prob(float,float){};
  virtual float angle(float){};
  virtual float en(float){ cout << "Absolutely nothing!\n";};
};

.......

int main()
{

  Mech1 mech;
  mech.prob(2.1,3.1);
  mech.angle(2.1);
  mech.en(2.0);

  Basic b = mech;
  b.en(0);

  return 0;
}

выдает 

$ ./virvir 
Mech1 angle
Mech1 en
Absolutely nothing!

Но если сделать Basic чисто виртуальным, 

class Basic{
public:

  virtual float prob(float,float)=0;
  virtual float angle(float)=0;
  virtual float en(float)=0;
};


то вот эта строчка

Basic b = mech;

станет нелегальной.

Uncle_Theodore ★★
()
Ответ на: комментарий от anonymous

> Не у меня объекты создаются произвольных классов естественно....

Нельзя создавать объекты виртуальных классов. Напиши это десять раз на доске. :) Можно создавать указатели на объекты виртуальных классов. В этом весь Большой Смысл. ;)

Uncle_Theodore ★★
()
Ответ на: комментарий от naryl

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

anonymous
()
Ответ на: комментарий от naryl

Basic *myObject = new Mech1();

myObject->en(0);

Слушай, не то, чтобы я заедаюсь, но new здесь не сработает. Вылетит ошибка еще на компиляции.

Что можно сделать, это вот что

Mech1 mech;

Basic *myOblect = &mech

myObject -> en(0);

Uncle_Theodore ★★
()
Ответ на: комментарий от Uncle_Theodore

не неправ, такое прокатывает:
std::vector<Basic*> vec;
vec.push_back(new Mech1);
а это равносильно твоей строчке
Basic *myObject = new Mech1();
или я лпять не прав?

anonymous
()
Ответ на: комментарий от naryl

Упс! Сорри, не дочитал... :) Мне показалось, там = new Basic 

Да, так все нормально.

Uncle_Theodore ★★
()
Ответ на: комментарий от Uncle_Theodore

Да ладно бывает, тем более что нормальные люди в такое время спят, а мы тут абстракцией данных страдаем ;)

anonymous
()
Ответ на: комментарий от Uncle_Theodore

> Автор -- попробуй поменять в Basic методы на чисто виртуальные.

А содно ещё и так:
class Basic{
public:

  virtual float prob(float,float)=0;
  virtual float angle(float)=0;
  virtual float en(float)=0;
protected:
  virtual ~Basic(){};
};

Ну или даже так:

class Basic{
public:

  virtual float prob(float,float)=0;
  virtual float angle(float)=0;
  virtual float en(float)=0;
protected:
  virtual ~Basic()=0;
};

// cpp
Basic::~Basic(){}

=)

stpg
()
Ответ на: комментарий от stpg

Чисто виртуальный деструктор. :) Ты знаешь, о нем очень любят спрашивать на интервью, когда на программиста хочешь устроиться. Не надо его создавать. Да и в данном случае, деструкторы не имеют отношения к вопросу.

Uncle_Theodore ★★
()
Ответ на: комментарий от Uncle_Theodore

А почему это не надо? Без ЧВД ведь не произойдёт цепочки вызовов деструкторов по иерархии классов при delete наследника.

one_more_hokum ★★★
()
Ответ на: комментарий от one_more_hokum

> А почему это не надо? Без ЧВД ведь не произойдёт цепочки вызовов деструкторов по иерархии классов при delete наследника.

Все прекрасно произойдет. Вот, посмотри. Прогони этот код, что он по-твоему выдаст? :)

#include<iostream>

using namespace std;

class Basic{
public:
  virtual float en(float)=0;
protected:
  virtual ~Basic(){cout << "Basic destructor\n";};
};


class Derived: public Basic{
public:
  float en(float);
  ~Derived(){cout << "Derived destructor!\n";};
};

float Derived::en(float x){cout << x << endl;};

int main(){
  Derived *der = new Derived;
  delete der;
  return 0;
}

Или даже вот так

class Basic{
public:
  virtual float en(float)=0;
protected:
  ~Basic(){cout << "Basic destructor\n";};
};

:)

А вот _чисто виртуальный_ деструктор -- невозможен. 
То есть, вот такая лабуда не сработает

#include<iostream>

using namespace std;

class Basic{
public:
  virtual float en(float)=0;
protected:
  virtual ~Basic()=0;
};


class Derived: public Basic{
public:
  float en(float);
  ~Derived(){cout << "Derived destructor!\n";};
};

float Derived::en(float x){cout << x << endl;};

int main(){
  Derived *der = new Derived;
  delete der;
  return 0;
}


Правда, в примере, на который я отвечал, там написано хитро. 
Но хитро лучше не писать, отлаживать сложнее...

Uncle_Theodore ★★
()
Ответ на: комментарий от one_more_hokum

> А нафея деструктор закрытым объявлять? А если он что-нить делать должен?

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

Чтото типа 

class Foo
{
protected:
  virtual ~Foo(){}
};

class Bar : public Foo
{
};

Если бы не было protected деструкторы то возможен такой код:

void test()
{
  Foo bar = Bar();
};

Как то так.

stpg
()
Ответ на: комментарий от one_more_hokum

> И всё-таки, почему ЧВД -- это не надо?

Потому, что ЧВД придумали чисто чтоб прикалыватся над начинающими прграмерами на С++.

Если в объявлении метода написано "=0" - то ты интуитивно предпологаешь, что его там нет.

А в случае с ЧВД в код деструктора в cpp можно написать хоть-что и чтоб узнать что происходит - надо будет изрядно попотеть;)

Например:

class Foo{
public:
  virtual ~Foo() = 0;
}


Foo::~Foo()
{
  std::abort(); // Веселого вам дебага сцуки
}

=)))

stpg
()
Ответ на: комментарий от stpg

У, блин! Какие нехорошие дяди плюсы разрабатывали! :-) Надо будет в проект вставить...

one_more_hokum ★★★
()
Ответ на: комментарий от stpg

А, кстати, чем это плохо? Ведь система виртуального наследования как раз и создавалась для того, чтобы в пределах иерархии можно было поменяв указателю вызвать метод соответствующего класса. Или я что-то не понял?

one_more_hokum ★★★
()
Ответ на: комментарий от one_more_hokum

> А, кстати, чем это плохо? Ведь система виртуального наследования как раз и создавалась для того

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

Bar bar = Bar();
Foo * barFoo = &bar;

Но это тяжелее чем 

Foo barFoo = Bar();

В общем хз;)

stpg
()
Ответ на: комментарий от Uncle_Theodore

>>Все прекрасно произойдет. Вот, посмотри. Прогони этот код, что он по-твоему выдаст? :)

Это обычный пример, без задействования полиморфизма.
С полимофизмом такое не прокатит:

#include <iostream>

using namespace std;

class Basic
{
  public:
    virtual float en(float)=0;
    ~Basic(){cout << "Basic destructor\n";};
};

class Derived : public Basic
{
  public:
    float en(float);
    ~Derived(){cout << "Derived destructor!\n";};
};

float Derived::en(float x) { cout << x << endl; }

int main(int, char **)
{
  Basic *der = new Derived;

  delete der;

  return 0;
}

---------

Без виртуального деструктора вызов цепочки деструкторов не произойдёт.

ЗЫ. ЧВД это да, интересный вопрос на собеседовании, наряду с виртуальным
 конструктором (в С++ ессно) :)

alex_custov ★★★★★
()
Ответ на: комментарий от alex_custov

>>>Все прекрасно произойдет. Вот, посмотри. Прогони этот код, что он по-твоему выдаст? :)

>Это обычный пример, без задействования полиморфизма. >С полимофизмом такое не прокатит:

во-во! зачем вообще полиморфизм(читай virtual), если им не пользуешься?

а вообще плохо здесь (в этой теме) со знаниями С++. очень плохо. много слов, а сути нет.

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

PS: за виртуальные конструкторы - респект)))

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.