Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по Программированию.doc
Скачиваний:
49
Добавлен:
11.02.2015
Размер:
1.22 Mб
Скачать

Копирующий конструктор

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

class x {

//………

};

x a;

x b=a;

x c(a);

x d=x(a);

Проектировщик класса может изменить такое поведение, предоставив специальный копирующий конструктор. Если он определен, то вызывается всякий раз, когда один объект инициализируется другим объектом того же класса.

Часто почленная инициализация не обеспечивает корректного поведения класса. Поэтому мы явно определяем копирующий конструктор. Копирующий конструктор принимает в качестве формального параметра ссылку на объект класса (традиционно объявляемую со спецификатором const).

class x {

//………

public:

x(const x &t);

//………

};

Пример:

#include <stdio.h>

class A

{

public:

int* a, N;

A(int);

void print();

};

A::A(int n)

{

a=new int[n];

N=n;

for(int i=0;i<N;i++)

a[i]=i;

}

void A::print()

{

for(int i=0;i<N;i++)

printf("%d ", a[i]);

printf("\n");

}

class B

{

public:

int* a, N;

B(int);

B(const B&);

void print();

};

B::B(int n)

{

a=new int[n];

N=n;

for(int i=0;i<N;i++)

a[i]=i;

}

B::B(const B &v)

{

N=v.N;

a=new int[N];

for(int i=0;i<N;i++)

a[i]=v.a[i];

}

void B::print()

{

for(int i=0;i<N;i++)

printf("%d ", a[i]);

printf("\n");

}

int main(int argc, char* argv[])

{

A a1(3);

A a2=a1;

a2.a[1]=8;

printf("Object a1 - ");

a1.print();

printf("Object a2 - ");

a2.print();

B b1(3);

B b2=b1;

b2.a[1]=8;

printf("Object b1 - ");

b1.print();

printf("Object b2 - ");

b2.print();

getchar();

return 0;

}

Вывод:

Object a1 - 0 8 2

Object a2 - 0 8 2

Object b1 - 0 1 2

Object b2 - 0 8 2

Очистка

Определяемый пользователем тип чаще имеет, чем не имеет, конструктор, который обеспечивает надлежащую инициализацию. Для многих типов также требуется обратное действие, деструктор, чтобы обеспечить соответствующую очистку объектов этого типа. Имя деструктора для класса X есть ~X() («дополнение конструктора»). В частности, многие типы используют некоторый объем динамической памяти, который выделяется конструктором и освобождается деструктором. Вот, например, традиционный стековый тип, из которого для краткости полностью выброшена обработка ошибок:

class char_stack {

int size;

char* top;

char* s;

public:

char_stack(int sz) { top=s=new char[size=sz]; }

~char_stack() { delete []s; } // деструктор

void push(char c) { *top++ = c; }

char pop() { return *--top;}

}

Когда char_stack выходит из области видимости, вызывается деструктор:

void f()

{

char_stack s1(100);

char_stack s2(200);

s1.push('a');

s2.push(s1.pop());

char ch = s2.pop();

printf("%c\n", ch;

}

Когда вызывается f(), конструктор char_stack вызывается для s1, чтобы выделить вектор из 100 символов, и для s2, чтобы выделить вектор из 200 символов. При возврате из f() эти два вектора будут освобождены.

Inline

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

Чтобы справиться с этой проблемой, был разработан аппарат inline-функций. Функция член, определенная (а не просто описанная) в описании класса, считается inline. Это значит, например, что в функциях, которые используют приведенные выше char_stack, нет никаких вызовов функций кроме тех, которые используются для реализации операций вывода! Другими словами, нет никаких затрат времени выполнения, которые стоит принимать во внимание при разработке класса. Любое, даже самое маленькое действие, можно задать эффективно. Это утверждение снимает аргумент, который чаще всего приводят чаще всего в пользу открытых членов данных.

Функцию член можно также описать как inline вне описания класса. Например:

char char_stack {

int size;

char* top;

char* s;

public:

char pop();

// ...

};

inline char char_stack::pop()

{

return *--top;

}