Читайте также: |
|
Так как конструкторы, деструктор и перегруженная операция присваивания не наследуются производным классом, то, если это необходимо, они должны быть определены непосредственно в производном классе.
При создании объекта производного класса вызывается конструктор базового класса, чтобы сначала “сконструировать” базовую часть класса. Конструктор базового класса, обычно, вызывается как часть списка инициализаторов в определении конструктора производного класса. Это вполне естественно – для завершения создания объекта сначала нужно создать объект базового класса. Затем конструктор производного класса “переключает свое внимание” на свои собственные данные-члены. Если какие-то параметры требуются конструктору базового класса, то они должны быть предоставлены в виде списка параметров конструктора производного класса (а в конструкторе базового, после двоеточия, они выступают в роли фактических параметров, см. пример выше: Leaf1(int a, int b, int c): Derived1(a, b, c) { }. Таким образом, в списке инициализации мы, по сути, осуществляем вызов конструктора базового класса). Если никаких параметров для конструктора базового объекта в конструкторе производного класса не указывается, то для базового класса вызывается конструктор по умолчанию. Но если в базовом классе присутствуют только конструкторы, которые требуют некоторых параметров, то производный класс должен иметь конструктор, имеющий в списке эти параметры, даже если сам производный класс не нуждается в (явном) конструкторе.
Поскольку деструктор не нуждается в каких-либо параметрах, то деструктор базового класса вызывается автоматически деструктором производного класса после завершения его части работы. В целом же, необходимость деструктора в производном классе возникает тогда, когда нужно освобождать какие-либо ресурсы, выделенные конструктором при создании объекта. Если явного деструктора в классе нет, то компилятор обеспечивает свой деструктор по умолчанию, в котором вызывается и деструктор базового класса. При уничтожении объекта деструкторы вызываются в обратном, по отношению к вызовам конструкторов, порядке. В следующем примере обратите внимание на то, как определены конструкторы (особенно, конструктор копирования), деструктор, функции, перегружающие операции присваивания и операцию вывода.
Пример 14-6.
#include <iostream.h>
#include <string.h>
class Base
{
protected:
int id;
char * name;
public:
// конструктор по умолчанию
Base(int a = 0, char * s = ""): id(a)
{
if (!s) { name = NULL; }
else
{
name = new char[strlen(s) + 1];
strcpy(name, s);
}
cout << "base default constructor\n";
}
// конструктор копирования
Base(const Base& b): id(b.id)
{
if (!b.name) { name = NULL; }
else
{
name = new char[strlen(b.name) + 1];
strcpy(name, b.name);
}
cout << "base copy constructor\n";
}
// деструктор
~Base()
{
delete [ ] name;
cout << "base destructor\n";
}
const Base& operator= (const Base& b);
friend ostream& operator << (ostream&, const Base&);
};
const Base& Base::operator= (const Base& b)
{
id = b.id;
if (this!= &b) // Проверяем случай присваивания объекта самому себе.
{
delete [ ] name; // Разрушаем старый объект.
if (!b.name) { name = NULL; }
else
{
name = new char[strlen(b.name) + 1];
strcpy(name, b.name);
}
}
cout << "base assignment operator\n";
return *this;
}
ostream& operator << (ostream& out, const Base& b)
{
out << "Base member id = " << b.id << endl;
out << "Base member name = " << b.name << endl;
return out;
}
class Derived: public Base
{
float f;
char * label;
public:
// конструктор по умолчанию
Derived(int a = 0, char * s = "", float x = 0, char * t = ""): Base(a, s), f(x)
{
if (!t) { label = NULL; }
else
{
label = new char [strlen(t) + 1];
strcpy(label, t);
}
cout << "derived default constructor\n";
}
// конструктор копирования
Derived(const Derived& d): Base(d), f(d.f)
// d используется как экземпляр класса Base
{
if (!d.label) { label = NULL; }
else
{
label = new char [strlen(d.label) + 1];
strcpy(label, d.label);
}
cout << "derived copy constructor\n";
}
// деструктор
~Derived()
{
delete [ ] label;
cout << "derived destructor\n";
}
const Derived& operator= (const Derived& d);
friend ostream& operator << (ostream&, const Derived&);
};
const Derived& Derived::operator= (const Derived& d)
{
if (this!= &d)
{
delete [ ] label;
Base::operator=(d); // Присваиваем базовую часть d (т.е., объект класса Base)
// базовой части объекта, который вызвал этот оператор;
f = d.f;
if (!d.label) { label = NULL; }
else
{
label = new char [strlen(d.label) + 1];
strcpy(label, d.label);
}
cout << "derived assignment operator\n";
}
return *this;
}
ostream& operator << (ostream& out, const Derived& d)
{
out << (Base)d; // Превращаем d в объект класса Base для вывода членов класса Base.
out << "Derived member f = " << d.f << endl;
out << "Derived member label = " << d.label << endl;
return out;
}
int main()
{
Derived d1, d2(d1);
return 0;
}
Программа выводит:
base default constructor // конструктор базовой части объекта d1
derived default constructor // конструктор дополнительной части d1
base copy constructor // копирование базовой части d1 в базовую часть объекта d2
derived copy constructor // копирование оставшейся части d1 в d2
derived destructor // вызван деструктор производного класса для разрушения d2
base destructor // вызван деструктор базового для разруш-ия базовой части d2
derived destructor // деструктор производного для разрушения d1
base destructor // деструктор базового для разрушения базовой части d1
Дата добавления: 2015-11-16; просмотров: 81 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Информация о типе во время выполнения (RTTI) (лучше почитать после обсуждения виртуальных функций). | | | Виртуальные функции |