Инкапсуляция в классах С++

Содержание

Слайд 2

Инкапсуляция при программировании классов Инкапсуляция данных при программировании классов – доступ

Инкапсуляция при программировании классов

Инкапсуляция данных при программировании классов – доступ к

свойствам через методы
Достоинства инкапсуляции
позволяет вводить инвариант представления для типа данных (упрощение разработки)
уменьшает зависимость кода пользователей класса от реализации методов и облегчает корректировку кода методов (сопровождение)
Требуются языковые средства, обеспечивающие полный контроль доступа
Слайд 3

Правила умолчания в С++ Присваивание объектов одного и того же типа

Правила умолчания в С++

Присваивание объектов одного и того же типа (побитовое

копирование памяти)
Создание объекта (отведение под структуру данных неинициализированной памяти)
Разрушение объекта (освобождение памяти, распределенной под структуру данных)
Инициализация объекта объектом того же типа (побитовое копирование памяти)
Вывод. Эти предопределенные в С++ операции
препятствуют принципу инкапсуляции свойств
Слайд 4

Проблема инварианта представления Инвариант представления для строки типа String – наличие

Проблема инварианта представления

Инвариант представления для строки типа String – наличие терминального

байта – не выполнен после создания объекта
Пример
#include “mystring.h”
#include
void fill_array( char* );
void main( int, char** ) {
String s, t ;
char very_big_text[1000] ;
fill_array(very_big_text);
for( int i = 0 ; i < s.length() ; i++ ) s[i] = very_big_text[i] ;
t = s ;
}
Слайд 5

Проблемы инкапсуляции Для классов с динамическим управлением памятью предопределенные в С++

Проблемы инкапсуляции

Для классов с динамическим управлением памятью предопределенные в С++ операции

будут вызывать проблемы
Пример

class String {
public:
void setString(char* ) ;
void freeString( ) ;
private:
char* ps;
};
void String::freeString(void)
{ delete ps ; }

void String::setString(char* s) {
ps=new char[strlen(s)+1];
if( ps == NULL ) exit(1);
else strcpy(ps, s);
}
void main ( int, char* [ ] ) {
String a;
a.setString(“Hello”);
String b, c=a;
b = a; a.freeStrinf();
b.freeString(); c.freeString();
}

Слайд 6

Проблема снимается перегрузкой операции присваивания для аргумента типа класса typename& operator=(

Проблема снимается

перегрузкой операции присваивания для аргумента типа класса
typename& operator=( const

typename& )
конструкторами класса (создание и инициализация объектов, передача аргументов и возврат значений)
деструктором класса (разрушение объекта)
Слайд 7

const int max_string_length = 128; class String { public: String( )

const int max_string_length = 128;
class String {
public:
String( ) ; // конструктор

по умолчанию
~String( ) ; // деструктор
String& operator= ( const String& );
String& operator= ( const char* );
int length( ) const ;
int read( ); // чтение из stdin
void print( ) const; // вывод в stdout
char& operator[ ] ( int );
const char& operator[ ] ( int ) const;
String substring (int start, int len) const;
friend String operator+ (const String&, const String& );
friend String operator+ (const String&, const char* );
friend String operator+ (const char*, const String& );
private:
char text [ max_string_length+1 ];
};
Слайд 8

Определение присваивания #include “mystring.h” #include // использование: s = “Hello World”

Определение присваивания

#include “mystring.h”
#include
// использование: s = “Hello World”
String& String::operator=(const

char* s ) {
strncpy( text, s, max_string_length );
text [max_string_length] = ‘\0’;
return *this;
}
// использование: s = t
String& String::operator=(const String& s ) {
strcpy( text, s.text ); // или *this = s.text;
return *this;
}
Слайд 9

Конструктор по умолчанию и пустой деструктор #include “mystring.h” // Конструктор по

Конструктор по умолчанию и пустой деструктор

#include “mystring.h”
// Конструктор по умолчанию обеспечивает инвариант

// представления при простом описании переменной
String::String( ) {
text[0] = ‘\0’ ;
}
// Теперь для переменной, описанной как String s;
// s.length() вернет 0
// Деструктору ничего не надо делать (его м. опустить)
String::~String( ) { }
Слайд 10

Конструкторы Конструктор – особая функция-член класса имя совпадает с именем класса

Конструкторы

Конструктор – особая функция-член класса
имя совпадает с именем класса
не возвращает значения
неявно

вызывается при создании объекта
вызов происходит сразу после выделения память под структуру ⇒ для ряда объектов (глобальные, внешние статические) конструктор вызывается до того, как функция main получит управление
Основное назначение конструкторов – это обеспечить корректность объекта при его создании и инициализации (в т.ч. при передаче аргументов и возврате значений)
Слайд 11

Деструктор Деструктор – особая функция-член класса имя образуется из тильды “~”

Деструктор

Деструктор – особая функция-член класса
имя образуется из тильды “~” и имени

класса
не возвращает значения
не имеет аргументов
неявно вызывается при разрушении объекта
вызов происходит непосредственно перед освобождением памяти под структуру ⇒ для ряда объектов (глобальные, статические) деструктор вызывается после того, как управление получит функция main
Основное назначение деструктора – это обеспечить корректное удаление объекта при завершении его времени жизни
Слайд 12

Вызов конструкторов и деструкторов #include “mystring.h” #include String s_extrn ; static

Вызов конструкторов и деструкторов

#include “mystring.h”
#include
String s_extrn ;
static String s_stat ;
void

example( ) {
printf( “Entering the example() function.\n” );
String s_auto ;
static String s_loc_stat ;
String s_array[30] ;
printf( “Leaving the example() function. \n” );
}
void main( int, char** ) {
printf( “Start of the program. \n” );
example( ) ;
example( ) ;
printf( “End of the program. \n” );
}
Слайд 13

Инициализация Отличие инициализации и присваивания Пример int i = 7; //

Инициализация

Отличие инициализации и присваивания
Пример int i = 7; // инициализация
int k;
k = 75000;

// присваивание
i = 0; // присваивание
Два синтаксических способа инициализации
Пример
# include “complex.h”
complex c = 7.4; // инициализация 7.4+0i
complex d (8.5); // инициализация 8.5+0i
complex e (9.1,10.0); // инициализация 9.1+10.0i
complex f ;
Слайд 14

Конструкторы и инициализация Инициализацию обеспечивает конструктор Пример Класс complex должен иметь

Конструкторы и инициализация

Инициализацию обеспечивает конструктор
Пример Класс complex должен иметь такие

конструкторы:
complex ( double ); // для инициализации 1 и 2
complex (double, double ); // для инициализации 3
complex ( ) ; // для описания без инициализации
или один конструктор вида
complex (double=0, double=0 ); // для всех описаний
Поместив соответствующий конструктор в закрытую часть класса можно запретить конкретный синтаксис описания
Слайд 15

Пример для класса String #include “mystring.h” #include void main ( int,

Пример для класса String

#include “mystring.h”
#include
void main ( int, char*[ ]

) {
String output ;
// Здесь требуется String::String( ) или отсутствие конструктора
String firstname = “Bilbo” ;
String middlename(“J.”) ;
// В этих случаях требуется String::String( const char* )
String lastname = firstname ;
String name( firstname +“ “+middlename+“ “+lastname);
// Здесь требуется String::String( const String& )
// или отсутствие конструктора вообще
output = “Name is: ” + name ; // operator=(const String&)
output.print() ;
}
Слайд 16

Класс может не иметь конструктора. В этом случае создание объекта состоит

Класс может не иметь конструктора. В этом случае создание объекта состоит

в выделении памяти под структуру
Класс может не иметь деструктора. В этом случае разрушение объекта состоит в освобождении памяти, выделенной под структуру
Класс может иметь несколько конструкторов. Если есть хотя бы один, то создание объекта требует конструктора
Класс может иметь только один деструктор.

Особенности конструкторов и деструкторов

Слайд 17

Конструктор без аргументов называется конструктором по умолчанию Конструктор с аргументом типа

Конструктор без аргументов называется конструктором по умолчанию
Конструктор с аргументом типа класса

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

Особенности конструкторов и деструкторов

Слайд 18

Расширение класса String const int max_string_length = 128; class String {

Расширение класса String

const int max_string_length = 128;
class String {
public:
String() ;
String( const

char* ) ;
String( const String& ) ;
~String() ;
String& operator= ( const char* );
int length( ) const ;
int read( ); // чтение из stdin
void print( ) const; // вывод в stdout
char& operator[ ] ( int );
const char& operator[ ] ( int ) const;
String substring (int start, int len) const;
friend String operator+ (const String&, const String& );
friend String operator+ (const String&, const char* );
friend String operator+ (const char*, const String& );
private:
char text [ max_string_length+1 ];
};
Слайд 19

Дополнительные конструкторы #include “mystring.h” #include // Конструктор с параметром String::String( const

Дополнительные конструкторы

#include “mystring.h”
#include
// Конструктор с параметром
String::String( const char* s)

{
strncpy( text, s, max_string_length ) ;
text [ max_string_length ] = ‘\0’
}
// Копирующий конструктор
String::String( const String& s) {
strcpy( text, s.text ) ;
text [ max_string_length ] = ‘\0’
}
Слайд 20

Свойства – данные типа класса Класс может иметь члены-данные типа класса.

Свойства – данные типа класса

Класс может иметь члены-данные типа класса.

#include

“mystring.h”
class Employee {
public:
void set_name( const String& );
String get_name( ) const ;
void set_salary( float );
float get_salary( ) const ;
// ……………
private:
String name;
float salary ;
};

Какие у него права относительно таких данных?

Слайд 21

Создание объекта типа Employee включает в себя создание свойства name типа

Создание объекта типа Employee включает в себя создание свойства name типа

String
Класс String имеет конструктор, который должен вызываться при создании объектов типа String
Следовательно, объект типа Employee создается в такой последовательности:
выделение памяти под структуру в порядке описания полей
после выделения памяти свойству (например, name) вызывается конструктор, если он есть (String::String() для name)
вызывается конструктор класса-владельца, если он есть

Создание объекта типа Employee

Слайд 22

Если у класса нет операции присваивания, то присваивание объектов типа Employee

Если у класса нет операции присваивания, то присваивание объектов типа Employee

происходит по правилам умолчания, т.е. копируется содержимое памяти
НО! Если свойства – данные типа класса, имеющего операцию присваивания, то для них будет использоваться эта операция присваивания (String::operator=(const String&) для name)

Присваивание объекта типа Employee

Слайд 23

Если у класса нет копирующего конструктора, то инициализация объектов типа Employee

Если у класса нет копирующего конструктора, то инициализация объектов типа Employee

происходит по правилам умолчания, т.е. копируется содержимое памяти
НО! Если свойства – данные типа класса, имеющего копирующий конструктор, то он будет использован для инициализации этих свойств (String::String(const String&) для name)

Инициализация объекта типа Employee

Слайд 24

Разрушение объекта типа Employee включает в себя разрушение свойства name типа

Разрушение объекта типа Employee включает в себя разрушение свойства name типа

String
Класс String имеет деструктор, который должен вызываться при разрушении объектов типа String
Следовательно, объект типа Employee разрушается такой последовательности:
вызывается деструктор класса-владельца (Employee), если он есть
освобождается память, отведенная под структуру в порядке, обратном описанию полей
до освобождением памяти для свойства (например, name) вызывается деструктор, если он есть (String::~String() для name)

Разрушение объекта типа Employee

Слайд 25

Пример использования #include “employee.h” int main ( int, char*[ ] )

Пример использования

#include “employee.h”
int main ( int, char*[ ] ) {
String

jones = “Jones” ;
Employee e1, e2 ; // String::String( ) применяется к // e1.name и e2.name
e1.set_name( jones ) ;
e1.set_salary( 3000 ) ;
e2 = e1 ; // String::operator=( const String & ) используется // для присваивания e2.name значения e2.name
Employee e3=e2 ; // String::String(const String&) // применяется для инициализации // e3.name значением e2.name
return 0; // String::~String( ) применяется к e1.name, // e2.name и e3.name
}
Слайд 26

Подмена встроенных операций Класс-владелец может иметь свои операцию присваивания конструктор по

Подмена встроенных операций

Класс-владелец может иметь свои
операцию присваивания
конструктор по умолчанию
копирующий конструктор
конструктор

с параметрами
деструктор
Слайд 27

Подмена встроенных операций #include “mystring.h” class Employee { public: Employee( );

Подмена встроенных операций

#include “mystring.h”
class Employee {
public:
Employee( );
Employee( const Employee&

);
Employee( const String& , float );
~Employee( );
Employee& operator=(const Employee&);
void set_name( const String& );
String get_name( ) const ;
void set_salary( float );
float get_salary( ) const ;
// ……………
private:
String name;
float salary ;
};
Слайд 28

Определение операций #include “Employee.h” Employee::Employee( ) { } Employee::Employee( const Employee&

Определение операций
#include “Employee.h”
Employee::Employee( ) { }
Employee::Employee( const Employee& e )

: name(e.name), salary(e.salary) { }
Employee::Employee( const String& n, float f)
: name(n), salary(f) { }
Employee::~Employee( ) { }
Employee::Employee& operator=(const Employee& e) {
name = e.name;
salary = e.salary;
return *this ;
}
Слайд 29

Определите семантику вызовов #include “employee.h” int main ( int, char*[ ]

Определите семантику вызовов

#include “employee.h”
int main ( int, char*[ ] ) {

String jones = “Jones” ;
Employee e1( jones , 3000 ) ; // ?
Employee e2 ; // ?
e2 = e1 ; // ?
Employee e3=e2 ; // ?
return 0; // ?
}
Слайд 30

Ответ #include “employee.h” int main ( int, char*[ ] ) {

Ответ

#include “employee.h”
int main ( int, char*[ ] ) {
String jones

= “Jones” ;
Employee e1( jones , 3000 ) ; // String::String(const String& )
// и Employee::Employee(const String&, float)
Employee e2 ; // String::String( ) и Employee::Employee( )
e2 = e1 ; // Employee::operator=( const Employee & )
Employee e3=e2 ; // String::String(const String&) и // Employee::Employee(const Employee&)
return 0; // Employee::~Employee( ) и затем String::~String( )
}
Слайд 31

РЕЗЮМЕ Язык С++ имеет все средства обеспечения при программировании класса полного

РЕЗЮМЕ

Язык С++ имеет все средства обеспечения при программировании класса полного контроля

над свойствами объекта
Для этого используются
методы класса
функции-друзья класса
А также средства корректировки разрешенных в языке С по умолчанию операций
присваивания: String& String::operator=(const String&)
сoздания: String:: String( )
инициализации: String:: String(const String&)
уничтожения: String:: ~String( )
Эти средства корректно работают с членами-данными типа класса