Текущий архив: 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.02 c