Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2008.08.24;
Скачать: CL | DM;

Вниз

Объясните правила?   Найти похожие ветки 

 
@!!ex ©   (2008-07-08 15:33) [0]

Есть три правила ООП в С++
не вызывать виртуальные функции из конструкора
не вызывать функции выделения памяти из конструктора
деструкторы должны быть виртуальными

Мне может кто нить объяснить, сакральный смысл этих правил? Особенно первых двух.


 
guav ©   (2008-07-08 15:41) [1]

> [0] @!!ex ©   (08.07.08 15:33)
> не вызывать виртуальные функции из конструкора

Можно вызывать. Надо только помнить что будут вызваны реализации именно для того класса, из конструктора которого они вызваны, а не для наследника, т.к. наследника ещё нет. Oдно из важных отличий от Delphi.


> [0] @!!ex ©   (08.07.08 15:33)
> не вызывать функции выделения памяти из конструктора

Не согласен


> [0] @!!ex ©   (08.07.08 15:33)
> деструкторы должны быть виртуальными

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

При удалении наследника через указатель на базу будет UB, если деструктор на базу не виртуальный.


 
Dimka Maslov ©   (2008-07-08 15:44) [2]

1. Конструктор суть метод, вызываемый до создания таблицы виртуальных методов. Если вызвать виртуальный метод из конструктора, то будет вызван метод самого класса, а не переопределённый в потомке.
2. Всегда вызывал и всегда работало. Главное в деструкторе эту память высвободить.
3. Могут быть и не виртуальными. Но для работы RTTI хотя бы один метод класса должен быть виртуальным. Чтобы не тянуть за уши виртуальность ненужного метода фиктивно виртуальным можно сделать деструктор. С другой стороны, деструктор в С++ вызывается в неявном виде. Для того, чтобы система имела информацию о том, какой метод данного объекта является деструктором, он должен быть виртуальным и его положение ссылки на него в таблице виртуальных методов должно быть фиксировано.


 
wl ©   (2008-07-08 15:48) [3]


> не вызывать функции выделения памяти из конструктора

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


 
guav ©   (2008-07-08 15:56) [4]

> [3] wl ©   (08.07.08 15:48)
> скорее не функции выделения памяти, а вообще любые, которые
> могут привести к исключениям. В таком случае деструктор
> не вызовется (так как объект не был построен) что приведет
> к утечке занятых ресурсов.

RAII спасёт нас.


 
guav ©   (2008-07-08 15:59) [5]

> [2] Dimka Maslov ©   (08.07.08 15:44)
> 3. Могут быть и не виртуальными. Но для работы RTTI хотя
> бы один метод класса должен быть виртуальным.

Нет, тут другое

struct base
{
...
}

struct derived : base
{
...
}

base *p = new derived;
delete p; // тут UB если деструктор у базы невиртуальный.


 
Alkid   (2008-07-08 15:59) [6]


> Можно вызывать. Надо только помнить что будут вызваны реализации
> именно для того класса, из конструктора которого они вызваны,
>  а не для наследника, т.к. наследника ещё нет. Oдно из важных
> отличий от Delphi.

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


 
ketmar ©   (2008-07-08 16:00) [7]

>[4] guav © (2008-07-08 15:56:00)
>RAII спасёт нас.

garbage collector спасёт. %-)

---
All Your Base Are Belong to Us


 
Игорь Шевченко ©   (2008-07-08 16:02) [8]

Какое отношение эти правила имеют к ООП ?


 
Alkid   (2008-07-08 16:04) [9]


> Какое отношение эти правила имеют к ООП ?

Очень простое - они имеют прямое отношение к реализации ООП в С++ => косвенное отношение к ООП.


> garbage collector спасёт. %-)

Да, особенно в стандартном С++ :)


 
guav ©   (2008-07-08 16:04) [10]

> [7] ketmar ©   (08.07.08 16:00)
> garbage collector спасёт. %-)

Да. Но тут речь таки об обычном С++.


 
Игорь Шевченко ©   (2008-07-08 16:05) [11]

Alkid   (08.07.08 16:04) [9]


> Очень простое - они имеют прямое отношение к реализации
> ООП в С++ => косвенное отношение к ООП.


С++ немножко не ООП, если кто не в курсе. Как насчет SmallTalk ?


 
Alkid ©   (2008-07-08 16:09) [12]


> С++ немножко не ООП, если кто не в курсе. Как насчет SmallTalk
> ?

Я в курсе, что нет единственно истинного определения ООП. Так что можно говорить, что оно есть и в С++ и в SmallTalk, но разного вкуса. Я и в Prolog могу внедрить ООП написав предикат в 4 срочки. Получится примитивное, но ООП :)
А ещё есть ООП в LISP с его контравариантными методами :)
Мир разнообразен :)


 
@!!ex ©   (2008-07-08 16:18) [13]

> [5] guav ©   (08.07.08 15:59)

А как в этом случае надо описать деструкторы?


> [8] Игорь Шевченко ©   (08.07.08 16:02)

К ООП в общем случае не имеют, поэтому я и написал что это в С++.


 
ketmar ©   (2008-07-08 16:18) [14]

>[10] guav © (2008-07-08 16:04:00)
>Да. Но тут речь таки об обычном С++.

тю. hans boehm conservative gc отлично к обычному цпп привинчивается. %-)

---
Do what thou wilt shall be the whole of the Law.


 
Alkid ©   (2008-07-08 16:21) [15]


> тю. hans boehm conservative gc отлично к обычному цпп привинчивается.
>  %-)

А ссылочку можно на него? Я в курсе, что есть gc для С++. У нас даже своего такого породили как-то. Однако сама суть С++ наталкивает меня на мысль, что для обращения с таким gc всегда надо будет придерживаться ряда правил, нарушение которых может приводить к разным пакостям от простых утечек (если gc грамотно спроектирован), до ухода всей системы вразнос (как у нас получилось :) )


 
Игорь Шевченко ©   (2008-07-08 16:29) [16]

@!!ex ©   (08.07.08 16:18) [13]

Я немножко про другое - это правила С++, а не ООП.


> не вызывать виртуальные функции из конструкора
> не вызывать функции выделения памяти из конструктора
> деструкторы должны быть виртуальными
>
> Мне может кто нить объяснить, сакральный смысл этих правил?
>  Особенно первых двух.


http://www.devdoc.ru/index.php/content/view/virtual_base.htm


 
@!!ex ©   (2008-07-08 16:30) [17]

> [16] Игорь Шевченко ©   (08.07.08 16:29)

Спасибо!


 
Riply ©   (2008-07-08 16:42) [18]

>  [16] Игорь Шевченко ©   (08.07.08 16:29)

Т.к. "спасибов" много не бывает, то добавлю еще одно :)


 
@!!ex ©   (2008-07-08 16:54) [19]

из статьи только одно не понял.
Как вызывать метод предка?
class A
{
 virtual void Test();
}

class B
{
 virtual void Test() {(A)this->Test(); };   //Чето мне кажется, что это не лучшее решение...
}


 
guav ©   (2008-07-08 16:56) [20]

> [13] @!!ex ©   (08.07.08 16:18)
> А как в этом случае надо описать деструкторы?

Если по RAII, то деструктор класса, который инкапсулирует захват одного ресурса содержит удаление одного ресурас. Исключение в конструкторе == ресурс не захвачен. Деструктор большого класса при этом станет пустой.


 
guav ©   (2008-07-08 16:56) [21]

> [19] @!!ex ©   (08.07.08 16:54)
A::Test()


 
guav ©   (2008-07-08 17:06) [22]

> [16] Игорь Шевченко ©   (08.07.08 16:29)
> http://www.devdoc.ru/index.php/content/view/virtual_base.htm


Ошибочка
В C++ существуют абстрактные виртуальные функции. Они определяются следующим образом:

virtual void foo() = 0;
Их особенность в том, что они не имеют тела. В таблице виртуальных функций они представлены с помощью нулевого указателя. Т.е. такая функция не может быть вызвана, точнее может, но это вызовет крах программы.


struct base
{
 virtual void f(void) = 0
 {
   puts("i"m base"); // это типа тело
 }

 virtual ~base(void)
 {
 }
};

struct derived : base
{
 void f(void)
 {
   base::f(); // вызвал, краха нет.
   puts("i"m derived");
 }
};

int _tmain(int argc, _TCHAR* argv[])
{
 derived d;
 d.f();
 return 0;
}



 
guav ©   (2008-07-08 17:08) [23]

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


 
Игорь Шевченко ©   (2008-07-08 17:09) [24]


> Ошибочка
> В C++ существуют абстрактные виртуальные функции. Они определяются
> следующим образом:
>
> virtual void foo() = 0;
> Их особенность в том, что они не имеют тела. В таблице виртуальных
> функций они представлены с помощью нулевого указателя. Т.
> е. такая функция не может быть вызвана, точнее может, но
> это вызовет крах программы.


А компилятор какой ?


 
oxffff ©   (2008-07-08 17:11) [25]


> guav ©   (08.07.08 17:08) [23]
> или вот виртуальный абстрактный деструктор фактически должен
> иметь тело, иначе при использовании экземпляров класса программа
> не будет линковаться.


Ну это вполне понятно. Поскольку именно при выходе из scope будет его вызов. :)


 
Alkid ©   (2008-07-08 17:14) [26]


> А компилятор какой ?

Вообще-то любой.
Это прописано в Стандарте.


 
guav ©   (2008-07-08 17:18) [27]

> [24] Игорь Шевченко ©   (08.07.08 17:09)

MSVC

В любом случае, даже если мой код в чём-то не легален, в С++ абстрактные функции могут иметь тело.


 
Игорь Шевченко ©   (2008-07-08 17:19) [28]

Alkid ©   (08.07.08 17:14) [26]

Я имел в виду, какой компилятор позволяет добиться того эффекта, какой в посте [22], или это тоже прописано в стандарте ?


 
Alkid ©   (2008-07-08 17:20) [29]


> Я имел в виду, какой компилятор позволяет добиться того
> эффекта, какой в посте [22], или это тоже прописано в стандарте
> ?

Это и прописано в стандарте. Абстрактная функция может иметь тело.
Более того, абстрактный деструктор ОБЯЗАН иметь тело, ибо его вызов генерирует автоматически компилятором.


 
guav ©   (2008-07-08 17:24) [30]

> [28] Игорь Шевченко ©   (08.07.08 17:19)

А что именно не так ?


 
Игорь Шевченко ©   (2008-07-08 17:28) [31]


> Это и прописано в стандарте. Абстрактная функция может иметь
> тело.


Век живи, век учись. Не знал


 
guav ©   (2008-07-08 17:37) [32]

Что ещё может быть интересно по коду [22]. Несмотря на то, что f объявлена без virtual, она перекрывает base::f - иначе derived не мог бы быть создан. Однако компилятор, скорее всего, не будет использовать vmt, т.к. base::f() - явно квалифицированный вызов, а при d.f(); наследник известен. Фактически, MSVC 2005 инлайнит оба вызова, т.е. получается main из двух puts.


 
guav ©   (2008-07-08 17:57) [33]

> [29] Alkid ©   (08.07.08 17:20)
> Более того, абстрактный деструктор ОБЯЗАН иметь тело,

Тоже не совсем так, обязан если мы хотим создавать экземпляры, а если не хотим - то не обязан.
struct no_instances
{
 static void hello(void) { puts("hello world!"); }
 virtual ~no_instances(void) = 0;
};

int _tmain(int argc, _TCHAR* argv[])
{
 no_instances::hello();
 return 0;
}


 
wl ©   (2008-07-08 19:19) [34]

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


 
guav ©   (2008-07-08 19:37) [35]

> [34] wl ©   (08.07.08 19:19)

UB вообще то :)

Если интересно посмотреть на это, можно попробовать добавить в деструктор base строку     reinterpret_cast<base*>(this)->f();
MSVC пишет
R6025
- pure virtual function call
и программа падает.


 
Alkid   (2008-07-08 23:59) [36]


> Тоже не совсем так, обязан если мы хотим создавать экземпляры,
>  а если не хотим - то не обязан.

Да, абсолютно правильное замечание. :)


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

А вот это уже, ЕМНИП, по стандарту даёт UB и то, что в MSVC он пишет такое, а не форматирует винт - это частный случай :)



Страницы: 1 вся ветка

Текущий архив: 2008.08.24;
Скачать: CL | DM;

Наверх




Память: 0.57 MB
Время: 0.019 c
6-1192218336
prisoner849
2007-10-12 23:45
2008.08.24
IdHTTP и Post в Indy 10 (BDS 2006)


15-1215422732
Nic
2008-07-07 13:25
2008.08.24
Лучшая программа управления лотком оптического привода


1-1197112192
Ольга
2007-12-08 14:09
2008.08.24
Отображение XML в Memo


15-1215082276
VLK32
2008-07-03 14:51
2008.08.24
Редактор кода в CodeGear RAD 2007


2-1216215791
savyhinst
2008-07-16 17:43
2008.08.24
TComponentName = type string;