Использование модификатора const
Модификатор const может использоваться для объявления функций-членов класса (селекторов), которые не будут изменять состояние класса и сообщают об этом компилятору. Это дает возможность использовать такие функции для экземпляров класса, объявленных как константы, например:
class X{
private:
int n;
public:
X(int a = 0){n = a;}
int getval(){return n;}
X& setval(int a){n = a; return *this;}
};
Если в программе есть:
X ob1(5);
const X ob2(5);
тогда:
ob1.setval(4); все в порядке;
ob2.setval(4); ошибка компиляции, т.к. значение константы не может быть изменено.
int k = ob1.getval(); все в порядке;
int l = ob2.getval(); ошибка компиляции (непредусмотренное использование константного объекта ob2), т.к. компилятор ничего не знает о том, что метод getval() не изменяет состояние адресата, а адресатом является константа.
Если изменить класс X следующим образом:
class X{
private:
int n;
public:
X(int a = 0){n = a;}
int getval() const {return n;}
X& setval(int a){n = a; return *this;}
};
Тогда все будет в порядке, и вызов int l = ob2.getval(); вернет корректное значение.
Если реализация метода приводится вне определения класса, тогда модификатор const должен появиться в двух местах: в прототипе функции-члена класса и в определении функции:
class X{
private:
int n;
public:
X(int a = 0){n = a;}
int getval() const;
X& setval(int a){n = a; return *this;}
};
int X::getval() const
{
return n;
}
Статические члены класса
Типы отношений между классами
Контейнерные классы: определение
Контейнерные классы: реализация методов
Контейнерные классы: реализация конструкторов
Производные классы: простое наследование
Основные понятия и определения: базовый класс и производные классы; производный класс есть базовый, обладающий дополнительными возможностями. Производный класс наследует все свойства базового класса и добавляет что-то свое. Отсюда, базовый класс - более абстрактное понятие (шире, чем производный); производный класс - более конкретное понятие (больше - по объему информации, чем базовый класс).
Отсюда:
базовый класс
собственные | собственные | |
Состояние - члены-данные | набор методов |
производный класс
от базового класса | от базового класса | |
собственные | собственные | |
Состояние - члены-данные | набор методов |
В производном классе имена методов (и членов - данных) могут совпадать с именами из базового класса.
Простое наследование: правила определения производного класса
Правила объявления производных классов. Две проблемы: могут ли (и как) методы производного класса обращаться к членам базового класса (доступность изнутри производного класса) и как члены базового класса, унаследованные производным классом, могут быть доступны извне производного класса.
Первая проблема решается уровнем видимости членов в базовом классе:
class B{
private:
// закрытая часть класса; не доступна никому, кроме методов класса (в том числе
// не доступна и из производного класса)
protected:
// защищенная часть класса; доступна из методов базового и производного класса
// (но не доступна извне класса)
public:
// открытая часть класса; доступна везде
};
Вторая проблема решается способом наследования:
class D: тип_наследования B{
. . .
};
тип_наследования: одно из private, protected, public.
Тип наследования не смягчает, но усиливает защиту данных: члены класса, имевшие менее защищенный уровень видимости в базовом классе, в производном классе приобретают уровень видимости, определяемый типом наследования.
тип наследования | |||
уровень видимости в базовом классе | private | protected | public |
protected | private | protected | protected |
public | private | protected | public |
Примеры.
Реализация конструкторов производного класса. Примеры.
Пример производного класса
Пример использования простого наследования: класс “точка” - базовый для класса “окружность”. Методы в классе “точка”: расстояние между двумя точками, вывод координат точки. Для класса окружность: свои дополнительные методы, отсутствовавшие в классе “точка” (пересечение окружностей); полностью наследуется метод - расстояние между точками (центрами двух окружностей) и требуется переопределить метод вывода окружности.
class Point{
protected:
int x, y;
public:
// конструкторы
Point():x(0), y(0){}
Point(int x0):x(x0),y(x0){}
Point(int x0, int y0):x(x0), y(y0){}
Point(const Point &p):x(p.x), y(p.y){}
void print();
float distance(Point p);
};
Реализация класса:
void Point::print()
{
cout << '(' << p.x << ", " << p.y << ')';
}
float Point::distance(Point p)
{
float dx = x - p.x;
float dy = y - p.y;
return sqrt(dx * dx + dy * dy);
}
Теперь - производный класс Окружность:
class Circle: public Point{
private:
int rad;
public:
// конструкторы
Circle():rad(0){} // Вызывается пустой конструктор базового класса
Circle(int r):rad(r){}
Circle(int x, int r):Point(x), rad(r){}
Circle(int x, int y, int r):Point(x, y), rad(r){}
Circle(Point p, int r):Point(p), rad(r){}
// методы
void print();
int intersect(Circle c);
};
Теперь - реализация методов
void Circle::print()
{
Point::print();
cout << " with rad " << rad;
}
int Circle::intersect(Circle c)
{
return distance(c) < rad + c.rad;
}
Метод distance из класса Point наследуется классом Circle.
Использование классов:
Circle c1(3), c2(2, 2);
cout << "distance between " << c1 << " and " << c2 << " is " << c1.distance(c2) << endl;
cout << c1 << " and " << c2;
if(c1.intersect(c2))
cout << " are intersected " << endl;
else
cout << " are not intersected " << endl;
Указатели на классы: базовый и производный
Представление экземпляров классов в памяти - производный класс = базовый плюс что-то свое:
class B{ . . . };
class D: public B { . . . };
B obj1; D obj2;
obj1 obj2
состояние часть базового
класса класса
собственная
часть
bPtr dPtr
Объявление указателей: B *b1Ptr, *b2Ptr; D *d1Ptr, *d2Ptr;
Указатели на тип определяют начальный адрес области памяти и ее размер, поэтому bPtr адресует только состояние, определяемое базовым классом, а dPtr - состояние, определяемое производным классом.
Определение указателей традиционное:
b1Ptr = &obj1; d1Ptr = &obj2; b2Ptr = b1Ptr; d2Ptr = d1Ptr;
b1Ptr = new B; d1Ptr = new D;
Можно указателю на базовый класс присвоить значение указателя на производный класс. В этом случае из производного класса будет использоваться только его часть, относящаяся к базовому классу:
b1Ptr = &obj2; b2Ptr = d1Ptr; b1Ptr = new D;
Обратное присваивание невозможно; записи вида d1Ptr = b1Ptr; вызовут сообщения об ошибках на этапе компиляции (присваивания указателей разного типа).
Вызов методов по указателю на класс
class B{ class D: public B{
. . . . . .
public: public:
. . . . . .
void f(); void f();
} }
D obj2, *dPtr = &obj2; B obj1, *b1Ptr = &obj1, *b2Ptr = &obj2;
Имеем следующее представление:
obj1 obj2
состояние часть базового
класса класса
собственная
часть
b1Ptr b2Ptr dPtr
Выполняем методы:
obj1.f() Вызывается метод f()базового класса для экземпляра базового класса obj1.
b1Ptr->f() Вызывается метод f()базового класса по указателю базового класса b1Ptr на экземпляр базового класса obj1.
obj2.f() Вызывается метод f()производного класса для экземпляра производного класса класса obj2.
dPtr->f() Вызывается метод f()производного класса по указателю производного класса dPtr на экземпляр производного класса obj2.
b2Ptr->f() Какой метод f()- базового или производного класса - вызывается здесь по указателю b2Ptr на экземпляр obj2 производного класса?