Определение и использование классов — КиберПедия 

Кормораздатчик мобильный электрифицированный: схема и процесс работы устройства...

Типы сооружений для обработки осадков: Септиками называются сооружения, в которых одновременно происходят осветление сточной жидкости...

Определение и использование классов

2022-10-27 32
Определение и использование классов 0.00 из 5.00 0 оценок
Заказать работу

 

Сущность объектно-ориентированного подхода. Для работы с десятичными дробями в программировании можем объявить переменную типа float и использовать стандартные операции ввода-вывода, сложения, вычитания и так далее. Если же необходимо для решения задачи воспользоваться простыми дробями, то программисту так же хотелось бы объявить одну переменную, в которой бы хранились и числитель, и знаменатель, использовать стандартные операции ввода-вывода, сложения и так далее, создавать массивы простых дробей. Однако встроенного типа данных для работы с простыми дробями в языке С++ нет. Реализовать желаемое дает возможность другая в отличии от структурного программирования методология – объектно-ориентированное программирование(ООП, Object-Oriented Programming) – совокупность принципов, технологий, а также инструментальных средств для создания программного обеспечения на основе архитектуры взаимодействия объектов.Другими словами разработчик выделяет из предметной области объекты, которые будут использоваться в программе, описывает их (определяет их свойства и методы)в виде классов и далее использует эти классы в программе, создавая и задавая действия над объеками определенных классов. Объект в ООП –некоторая сущность из реального мира в компьютерном пространстве, обладающая определённым состоянием и поведением, имеющая заданные значения свойств (атрибутов) и операций над ними (методов), способная сохранять свое состояние (информацию) и обеспечивающая набор операций (поведение) для проверки и изменения этого состояния. Свойства – это некоторые характеристики объекта, например, длина, ширина. Другими словами, некоторые исходные данные, характеризующие объект. Под методами объекта понимают процедуры и функции, объявление которых включено в определениекласса и которые выполняют действия. Объектом в нашем примере выступит «простая дробь». Свойства объекта – числитель и знаменатель. Методы – ввод, вывод, умножение дроби на целое число. В языке С++ в качестве методов обработки данных выступают функции, а для хранения свойств используются переменные. Таким образом, класс в С++ объединяет в себе переменные и функции, обрабатывающие эти переменные. Совокупность методов часто называют интерфейсом объекта.

Синтаксис определения классов исоздания объектов. Реализация объектов в языке С++ осуществляется через классы. Класс – это структурированный тип данных, включающий в себе в качестве элементов типизированные данные и функции, применяемые по отношению к этим данным. Синтаксис определения класса:

class имя {

тип1 переменная-свойство1;

тип2 переменная-свойство2;

public:

тип функция-метод1(параметры);

тип функция-метод2(параметры);

};

Определение класса обязательно заканчивается «;», т.к. это оператор.

Если при определении класса свойство помечено ключевым словом conts, то оно становится константным и далее в программе нельзя изменять его значение.

Определение функций-членов класса обычно производится отдельноот определения класса. Программный код функций пишетсяза пределами класса после его определения. При этом нужно указывать, к какому классу принадлежит данная функция. Знак операции принадлежности – «::» или операция глобального разрешения.

Формат определения члена-функции:

тип имя_класса::имя_функции(параметры){тело функции}

При определении класса объекты не создаются, а определяется только их внешний вид. Можно сказать, что объект – это переменная определенного пользователем класса или типа данных. Таким образом, объекты класса определяются как и обычные переменные:

имя_класса имя_объекта1, имя_объекта 2;

После того как в основной части программы класс поставлен в соответствие определенным переменным, т.е. созданы объекты (или экземпляры) класса, обращение к элементам объектов производится с помощью составного имени через «.» – оператор доступа к члену класса.

имя_класса.имя_переменной-объекта;

имя_переменной-объекта.имя_функции-метода();//вызов метода

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

Написание программ в стиле ООП предполагает следующие этапы:

0 (этап проектирования) выявить в предметной области объекты, участвующие в решении задачи и связи между ними;

1 описать каждый объект в виде класса;

2 запрограммировать методы объекта;

3 запрограммировать алгоритм решения основной задачи.

Описываем объект дробь в виде класса fraction:

class fraction{

int m; // числитель

int n; // знаменатель

public: void input(); // ввод дроби

void output(); // вывод дроби

void mult(inta); // умножение дроби на целое число

string isProper(); // проверка, является ли дробь правильной

};

Программируем методы класса fraction:

void fraction::input(){ // ввод дроби

cout<< "Числитель";

cin>>m;

cout<< "Знаменатель";

cin>>n;

}

void fraction::output(){ // выводдроби

cout<<m<<"/"<<n;

}

void fraction::mult(int a){ // умножениедробиначисло

m=m*a;

}

string fraction:: isProper (){ //проверка, является ли дробь правильной

return (m<n)?"правильная":"неправильная";

}

Программируем алгоритм решения основной задачи

intmain (){

fractionA; //дробь

// вводдроби

cout<<"Введите дробь";

/*Чтобы ввести дробь А, нужно у объекта A вызвать метод input. */

A.input();

//умножение дроби на целое число

int k; // число на которое нужно умножить дробь

cout<< "Введите число, на которое нужно умножить дробь";

cin>>k;

/*Чтобы умножить дробь А на число k нужно у объекта A вызвать */

A.mult(k);//вызываем метод mult для дроби A

// вывод дроби

A.output();//вызываем метод output для дроби A

/*Чтобы определить, является ли дробь правильной, нужно у объекта А вызвать метод isProper() одновременно с выводом его результата на печать*/

cout<<" Дробь "<<A.isProper()<<endl;

}

Поскольку метод isProper () возвращает строковое значение, для которого реализованы стандартные средства вывода его на экран, то вызвать этот метод для объекта можно одновременно с операцией вывода. Метод output() ничего не возвращает. Более того внутри себя он содержит операции вывода данных. Поэтому использовать его одновременно с операцией вывода нельзя. То есть запись

cout <<"Дробь "<<A.output(); //вызовет ошибку компиляции

приведет к ошибке компиляции.

В большинстве программ, написанных на С++, выполняемые действие сводятся к вызову функцией main () различных методов определенных в программе объектов. Кроме того функция main () может вызывать другие независимые функции. Таким образом в программе на С++ функция может либо входить в состав класса – тогда она становится методом, либо не входить в состав класса и быть независимой функцией.В С++ внутри класса имя метода не может совпадать с именем свойства.

Ключевые слова private, protected, public определяют режим доступа (accessmode) к членам класса. Они могут быть использованы в определении класса многократно и в произвольном порядке. Однако хорошим стилем программирования считается использование каждого из этих ключевых слов один раз.

Элементы private не доступны за пределами класса. Т.е. они не могут использоваться никакими функциями, не являющимися членами класса. Также private -функциимогут вызываться только функциями-членами класса. Переменные и функции, объявленные после ключевого слова public, доступны для всех других функций программы, т. е. остальная часть программы имеет доступ к элементам объекта через public переменные и функции. Такие члены класса предназначены для обеспечения интерфейса объектов класса с программой, в которой они существуют. Как правило, большинство данных объявляются private, а доступ к ним осуществляется через public функции. Режим доступа protected (защищенный) играет существенную роль при использовании механизма наследования классов. Проектируя класс, необходимо тщательно продумать, какие его члены сделать открытыми, а какие – закрытыми. При определении класса с помощью ключевого слова class его члены будут считаться по умолчанию закрытыми (private). В связи с выше сказанным операторы приведут к ошибке компиляции:

cin >> A. m >> A. n; // недопустимые операции

A. m =1; A. n =2; // недопустимые операции

cout << A. m << A. n; // недопустимые операции

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

В С++ можно создавать и использовать массивы объектов. Принцип объявления и использования их схож с массивами встроенных типов данных.

Пример.

fraction B[4]; // массив дробей

// вводмассивадробей

for (inti=0;i<4;i++){

B[i].input();

}

// выводмассивадробей

for (int i=0;i<4;i++){

B [ i ]. output ();

}

Доступ к объектам, находящимся в массиве осуществляется через индексные имена, и далее через точку можно обращаться к компонентам объекта.

Доступ к объектам можно получить через указатель. В этом случае к элементам объекта обращаются с помощью оператора ->.

fraction A; // дробь

// объявляем указатель типа fraction и присваиваем ему адрес дроби A

fraction* p=&A;

p->input();// вводдроби

p -> output (); // выводдроби

UML -диаграмма классов. Для наглядного представления проекта, состоящего из множества классов можно использоватьUML-диаграммы. Класс на них представляется в виде прямоугольника, разделенного на три части (рисунок 2.1).В верхней части указывается название, в средней – свойства, в нижней методы. Режим доступа указывается перед названием члена класса с помощью следующих обозначений:

-– private;

+ – public;

# – protected.

 

 

Рисунок 2.1 – Структура класса на UML-диаграмме

 

На рисунке 4.2 представлены примеры классов.

 

 

Рисунок 2.2 – Примеры классов

 

Класс student, описывающий объект«студент», имеет два закрытых поля, отражающих его имя (name) и уровень знаний и умений (garde), а также два открытых метода для получения имени (getName ()) и вывода уровня (printGrade ()).Класс circle, описывающий объект«круг», содержит два закрытых свойства радиус (radius) и цвет (color), а также два открытых метода для получения значения радиуса (getRadius ()) и вычисления площади (getArea ()).Класс soccerPlayer, описывающий объект «футболист», содержит своства: имя (name), номер (number), а также положение по осям х (xLocation) и у (yLocation); методы: бежит (run ()), прыгает (jump ()). Класс car, описывающий объект «легковой автомобиль», содержит такие характеристики как номерной знак (plateNumber), скорость (speed), положение по осям х (xLocation) и у (yLocation); методы движится (move ()) и паркуется (park ()).

Свойства объектно-ориентированного программирования. В основу объектно-ориентированных языков положены три важнейших принципа: инкапсуляция, наследованием, полиморфизм.

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

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

Полиморфизм – это свойство, которое позволяет одно и тоже имя использовать для решения нескольких технически разных задач. Т. е. имя определяет класс (область) действий, которые в зависимости от типа данных могут существенно отличаться. Например, можно определить три типа переменных: целые, комплексные числа и векторы. Для каждого из них можно определить функцию sum(х, у) – сумму двух переменных, а можно сделать так, что для этих трех типов будет определена операция сложения х + у. В зависимости от того, какого типа будут переменные х и у, работать эта функция и операция сложения будут по-разному. Причем полиморфизм поддерживается в С++ и во время компиляции, и во время выполнения программы.

 

Контрольные вопросы и задания

1.  Перечислите, из чего состоит класс в С++? Объясните разницу в терминах «объект» и «класс»?

2.  Приведите синтаксис определения класса и описания методов.

3.  Как в главной программе объявить объект и вызвать метод?

4.  На что влияет режим доступа к элементам объекта?

5.  Как представляются классы на UML-диаграммах? Составить UML-диаграмму класса fraction.

6.  Как объявить массив объектов и получить доступ к его элементам, а также их свойствам и методам?

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

8.  Назовите три основных принципа объектно-ориентированного программирования. Поясните на примере, что они означают.

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

10.  Создать класс (включая UML-диаграмму), описывающий объект «банковский счет». С использованием данного класса разработать программное обеспечение, позволяющие открыть счет клиенту, выполнять операции пополнения и снятия, а также просматривать баланс.

 

Методы

 

Перегружаемыефункции. Полиморфизм в С++ реализуется через механизм перегрузки. Внутри класса допускается существование нескольких функций с одинаковым именем, но различающимися наборами параметров. Такие функции реализуют разные алгоритмы в зависимости от типлв и количества передаваемых параметров и могут иметь разный тип возвращаемого значения.  Например, для класса fraction можно создать метод с одинаковый названием mult (), который будет выполнять умножение, но в зависимости от того на что надо умножать дробь, на целое число или на простую дробь, будут реализованы разные алгоритмы.

classfraction {…

voidmult (inta); // умножение дроби на целое число

voidmult (fractionobj);// умножение дроби на дробь

};

void fraction::mult(inta){ // умножениедробиначисло

m=m*a;

}

void fraction::mult(fractionobj){ // умножениедробиначисло

m = m * obj. m;/* m – свойство объекта 1-го множителя,который вызвал метод mult (); obj. m - свойство объекта 2-го множителя, который пришел параметром в функцию*/

n = n * obj. n;

}

В данном примере свойства m и n переданного в качестве параметра объекта obj доступны внутриметода, несмотря на то что они являются закрытыми в определении класса. Это происходит потому, что метод может напрямую (без использования операции «.») обращаться по имени к открытым и закрытым членам этого объекта. Кроме того метод имеет непрямой (через операцию «.») доступ к членам других объектов своего класса.

intmain(){

fractionA, B;

A.mult(B);/* будетвызванареализаия voidfraction::mult(fractionobj), таккакпараметр B типа fraction*/|

A.mult(2); /* будетвызванареализаия voidfraction::mult(inta), таккакаргумент 2 типа int*/

}

Особенностью использования перегруженных функций является то, что они не обязаны возвращать значения одинакового типа по той причине, что компилятор однозначно идентифицирует функцию по ее имени и набору аргументов. Для компилятора функции с одинаковыми именами, но различными типами аргументов – разные функции, поэтому тип возвращаемого значения – прерогатива каждой функции.

Встраиваемые функции. Определение функции-члена класса можно включить в определение класса. В этом случае функция становится встраиваемой. То есть она не вызывается, а ее тело встраивается в программу в место вызова, как макроопределение с параметрами в языке С. В зависимости от компилятора возможны ограничения на использование встраиваемых функций: не должна содержать циклов, switch, goto, статических переменных, не должна быть рекурсивной.

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

classfraction{…

intgetWhole()const{// возвращаетцелуючастьдроби

/*m = 0;

// error: assignmentofdata-member 'fraction::m' in read-only structure

*/

returnm / n;

};

};

Внутри константных методов могут быть вызваны только другие константные методы класса.

Методы доступа к закрытым членам класса (getter и setter –методы). Согласно принципам объектно-ориентированного программирования, данные класса должны быть скрыты из вне. Т.е. в программе нельзя получить напрямую доступ к закрытым переменным за пределами класса.

В определенномв предыдущем параграфе классе fraction свойства m и n объявлены как private,поэтому нельзя напрямую из главной программы обращаться к ним и изменять значения числителя и знаменателя дроби.

cin >> A. m >> A. n; // недопустимые операции

A. m =1; A. n =2; // недопустимые операции

cout << A. m << A. n; // недопустимые операции

Если необходимо получать или устанавливать значения свойств объекта, то в класс необходимо добавить специальные методыс идентификатором public и вызыватьих в функции main (), либо других функциях, где это будет необхоимо. Часто методы, предназначенные для присваивания значений называют setter -методами. Принято, что заголовок функции, реализующей такой метод, начинается со слова set и далее следует имя свойства класса, которому данный метод присвоит значение.Данные функции могут обеспечивать проверку данных (например, проверки диапазона), и преобразование исходных данных во внутреннее представление.Для того чтобы получить за пределами класса значение его свойств, создают getter -методы.Данные функции не должны изменять значения переменных-свойств объекта, но они могут их обработать,прежде чем выдать.Такие методы лучше объявлять как константные.Рассмотрим пример, демонстрирующий использование данных методов для класса fraction.

class fraction{

int m; // числитель

int n; // знаменатель

public: void input(); // ввод дроби

void output(); // вывод дроби

// getter -методы

intgetM () const { returnm;} //получить числитель

intgetN () const { returnn;} //получить знаменатель

// setter -методы

voidsetM (intm){ this -> m = m;} // изменение числителя дроби

voidsetN (int);// изменение знаменателя дроби

};

voidfraction::setN(intn){

if (n ==0) {//попытка присвоить знаменателю значение 0

cout <<"Знаменатель не может быть равным 0!";

exit (0);// вызовет завершение программы с ошибкой

}

this->n=n;

}

intmain(){

fractionA; // дробь

A.setM(1); A.setM(2); // присваиваемдробизначение 1/2

A.output(); // выводимдробь

cout>> " числительдроби " >>A.getM();

}

В данном примере методы getM (), getN () и setM () определены как встраиваемые функции.

В языке С++, как и во многих других языках объектно-ориентированного программирования можно использовать указатель this для того чтобы сослаться на текущий объект внутри класса. Рассмотрим его использование на примере разрешениянеоднозначностеймеждупеременными-свойствамиобъектаипеременными-параметрамифункции.

В представленном программном коде функции setM () два идентификатора имеют имя т – переменная-свойство и параметр функции. Возникает конфликт имен. Чтобы разрешить конфликт можно использовать имя параметра функции l вместо m. Однако m имеет более приближенное и смысловое значение в данном контексте. Для разрешения конфликта имен можно использовать указатель this для обращения к свойствам объекта. this -> m ссылается на свойство класса, в то время как m – параметр функции.

Как альтернативу, чтобы избежать конфликта имен можно использовать префиксы (такие как  m_) или суффиксы (такие как _). Пример:

voidsetM (intm _){ m = m _;} В компиляторахC++ имена своих внутренних переменных начинаются с «_», а локальных переменных с «__» (двойного подчеркивания). Следовательно, необходимоизбегатьименпеременных, начинающихсяс «_».

В методе setN (intn) реализована проверка корректности ввода данных, то есть знаменателю не может быть равным 0.

Можно усовершенствовать функцию ввода зачений свойств дроби:

void fraction::input(){ // ввод дроби

cout<< "Числитель"; cin>>m;

int denominator;

cout<< " Знаменатель "; cin>>denominator;

setN (denominator);

}

В этом случае контроль данных будет производиться и при попытке ввода данных. Причем алгоритм проверки реализован один раз в set -методе. Если будет необходимо изменить данный алгоритм, то это достаточно будет сделать автоматически в одном месте и он автоматически применится и при вводе данных. Ввод значения свойства m происходит без использования set -метода. В данном случае это сделано для ускорения работы программы, т.к. какая-либо проверка введенных значений не предполагается, а вызов метода повлечен дополнительные временные затраты.

При необходимости программист может создать более удобные методы по присваиванию значений свойств объектам – т.е. присвоить значения сразу всем свойствам, либо части свойств. Например, создадим метод setFraction (), который присвоит значения сразу и числителю и знаменателю дроби.

void setFraction(int m, int n){

setM (m);

setN (n);

}

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

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

Для дальнейших примеров будем рассматривать объект «куб», который в качестве свойств имеет размер ребра, а также цвет грани.

#include <string>

#include <iostream>

using namespace std;

class cube{

private:

float edgeLength = 1; // длинаребра  

const string edgeColor = "red"; // цветграни

public:

float getVolume () const;// объем

void setEdgeLength (float edgeLength);

void setEdgeColor(float edgeColor);

};

 float cube::getVolume () const {                  

return edgeLength * edgeLength * edgeLength;

}

void cube::setEdgeLength (float edgeLength) {

this -> edgeLength = edgeLength;

}

/*//присваиваниезначенияцветуграни куба

void cube::setEdgeColor(float edgeColor){

this -> edgeColor = edgeColor;/* ошибкакомпиляции – попыткаприсвоитьзначениеконстантномусвойству*/

}*/

intmain (){

const cubemyUnchangeCube; //объявленкакконстантныйобъект   

cube myChangeCube;

cout << "Объем myUnchangeCube " << myUnchangeCube.getVolume () << endl; //1

cout << "Объем myChangeCube " << myChangeCube.getVolume () << endl;

//изменение размера ребра куба

/* myUnchangeCube. setEdgeLength (2.0);//ошибка компиляции – попытка вызвать неконстантный метод для константного объекта*/

myChangeCube.setEdgeLength (2.0);

cout << "Объем myChangeCube " << myChangeCube.getVolume () << endl;

return 0;

}

Следует обратить внимание, что если метод getVolume () не будет объявлен как константный, то попытка вызвать его для объекта myUnchangeCube (в строке //1) приведет к ошибке компиляции.

Конструкторы и деструкторы. Конструктор – это специальный метод, который используется при создании экземпляров класса. Они производит выделения памяти под переменные-свойства, а также инициализирует их значения, дополнительно в нем может выполняться открытие файлов, а также установка параметров экрана.Чтобы создать новый экземпляр класса, необходимо объявить его имя и вызвать конструктор. Конструкторсрабатываетавтоматическипривыполненииоперацииопределениятипапеременной. Вызывается функция-конструкторавтоматически в тот момент, когда создается объект, т. е. для объекта выделяется место в памяти. Нельзя вызвать функцию-конструктор в явном виде. Создание нового объекта происходит не только при объявлении объектов классов. Новый объект может создаваться, например, при передаче параметров в функцию. Поэтому существует несколько специальных видов конструкторов, которые будут рассмотрены в данном параграфе. Деструктор – это специальный метод, который срабатывает при разрушении объекта, например, по завершении работы программы, удалении объекта из памяти с помощью оператора delete или при выходе из функции. Деструктор освобождает память, выделенную конструктором, закрывает открытые файлы, восстанавливает значение экрана. Области памяти, занятые данными базовых типов выделяются и освобождаются системой автоматически. Поэтому в примере выше конструкторы и деструкторы не объявлялись.

Конструктор и деструктор являются членами класса. Имя конструктора совпадает с именем класса. Имя деструктора начинается с символа ~, за которым следует имя класса.

Функция-конструктор как и функция-деструктор не могут иметь тип возвращаемого значения. Конструктор, как и другие функции, может иметь параметры. Класс может иметь несколько конструкторов. Если в классе не объявлен ни один конструктор, компилятор сам создает функцию-конструктор класса. По умолчанию создается конструктор без параметров имеющий режим доступа public. Деструктор не может иметь параметров. В следствии чего для него нельзя создать перегружаемую функцию. Т.е. деструктор в классе может быть только один. Ни конструктор, ни деструктор не вызываются явно. Если в классе не определен деструктор, то компилятор сам генерирует функцию-деструктор, которая просто освобождает память, занятую данными объекта. Таким образом, конструктор отличается от обычных функций следующим:

- Имя конструктора совпадает с именем класса.

- Конструктор не имеет типа возвращаемого значения (неявно возвращает void). Следовательно, использование оператора return не допускается в конструкторе.

- Конструктор может быть вызван только один раз при создании экземпляра класса. Его нельзя вызвать впоследствии в программе.

- Конструкторыненаследуются.

Добавим конструктор в определенный выше класс cube.

classcube{

float edge L ength;// реброкуба

string edgeColor;// цветграни

public:

cube(float l, string c);// конструктор

float getVolume();

};

Реализацияконструктора:

cube:: cube(float l, string c){// конструктор

edge L ength=l;

edgeColor=c;

}

intmain(){

cubeA(5," red ");// создали куб с длиной ребра 5 и цветом грани красным

cout<<A.getVolume();//выводим объем куба

}

Конструктор по умолчанию - это конструктор без параметров или имеющий значения по умолчанию для всех параметров. Для класса cube при попытке выполнить

с ube В;

компилятором будет выдано сообщение об ошибке error: nomatchingfunctionforcallto ' cube:: cube ()'

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

Если в классе не определено ни одного конструктора, то компилятор автоматически создает конструктор по умолчанию без параметров с пустым телом функции.

className:: className () { }

Длякласса cube:

cube:: cube (){}

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

Пример конструктора с аргументами по умолчанию

cube(float l=1, string c="red"){

edgeLength = l;

edgeColor = c;

}

При наличии такого конструктора в классе cube, после выполнения

с ube В;

будет создан объект «куб »В с длиной ребра 1 и красным цветом грани.

Вместо инициализации private членов класса в теле конструктора, как показано в данном примере, можно использовать альтернативный синтаксис, который имеет название memberinitializelist (список инициализируемых членов) и следующий синтаксис:

имя():имя_перемен1(значение), имя_перемен1(значение){}

Пример:

cube(floatl = 1.0, string c = "red"): edgeLength (l), edgeColor(c) { }

Список инициализируемых членов располагается после заголовка конструктора, через «:». Элементыспискаотделяютсядруготдруга «,».Каждый элемент списка записывается в форме: data_member_name(parameter_name). Для базовых типов данных это эквивалентно записи data_member_name = parameter_name. Для объектных типов данных (классов, созданных пользователями) будет вызван конструктор. Тело конструктора, в данном случае пустое, будет выполняться после завершения инициализации параметров.

Такой подход рекомендуется использовать для инициализации всех членов класса, так как это часто более эффективно, чем делать присваивание внутри тела конструктора.

В C ++ конструктор с одним аргументом может быть использован для неявного преобразования значения в объект.Пример:

#include<iostream>

using namespace std;

class counter {

private:

int count;

public:

/*Конструктор с одним аргументом, который принимает значение типа int используется, чтобы неявно конвертировать значение int в объект Counter */

counter(int c = 0): count(c) { }

int getCount() const { return count; }

void setCount(int c) { count = c; }

};

intmain () {

//Объявляется экземпляр класса и вызывается конструктор по умолчанию

counter c1;

cout << c1.getCount() << endl; // 0

/*Выполняется неявное преобразование. Вызывается конструктор с одним аргументом Counter (9), чтобы построить временный объект*/

c1 = 9;

cout<<c1.getCount() <<endl; // 9

}

Такое неявное преобразование может привести к путанице. В C++ существует ключевое слово " explicit ", чтобы отключить неявное преобразование. Тем не менее, все равно можно выполнить явное преобразование, используя оператор cast. Пример:

#include <iostream>

using namespace std;

class counter {

private:

int count;

public:

/*В данной реализации конструктора автоматическое преоразование отключено.*/

explicit counter(int c = 0): count(c) { }

int getCount() const { return count; }

void setCount(int c) { count = c; }

};

intmain () {

//Объявляется экземпляр класса, срабатывает конструктор по умолчанию

counter c1;

cout << c1.getCount() << endl; // 0

// counter c2 = 9;

// error: conversion from 'int' to non-scalar type ' с ounter' requested

c1 = (с ounter)9; // Явноепреобразование

cout << c1.getCount() << endl; // 9

}

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

Если конструктор копирования не определен, то компилятор создает конструктор по умолчанию, который копирует все элементы данных данного объекта.

Для определенного класса cube:

int main(){

cube A(5,"red"); // создали куб с длиной ребра 5 и цветом грани красным

cout<<A.getVolume();//выводим объем куба

cubeC (A); создали объект С, который является копией объекта А

cout << C. getVolume ();//выводим объем куба

}

Когда объект передается в функцию по значению, конструктор копирования будет использоваться для создания клон-копии аргумента.

Конструктор копии по умолчанию выполняет shadowcopy. Он не копирует динамически распределенные элементы данных, созданные с помощью new или new []. Таким образом, если память под переменные-свойства выделялась динамически, то необходимо разрабатывать конструктор копирования, который будет обрабатывать эти данные. Это будет рассмотрено позднее в теме перегразука операции присваивания.

Конструктор копирования объявляется следующим образом:

className::className(const className& obj) { }

Примердлякласса cube:

cube::cube(const cube&obj) { edgeLength= obj.edgeLength;       edgeColor= obj.edgeColor; } Передача в функцию по значению для объекта означает вызов конструктора копирования. Чтобы избежать накладных расходов на создание копии клона, обычно лучше передавать по константной ссылке, что не будет иметь побочного эффекта при изменении вызывающего объекта.

Оператор присваивания по умолчанию. Компилятор также предоставляет оператор присваивания по умолчанию (=), который может быть использован для назначения одного объекта другому объекту того же класса через экземплярную копию. Например,

cube c6(5.6, "orange"), c7;

cout<<"Edge L ength="<<c6.getEdge L ength()<<"Volume="<<c6.getVolume()<< " Color=" << c6.getColor() << endl;

// Edge L ength=5.6 Area=98.5206 Color=orange

cout << "Edge L ength=" << c7.getEdge L ength() << " Volume=" << c7.getVolume() << " Color=" << c7.getColor() << endl;

// Edge L ength =1 Volume =175.616 Color = red (конструктор по умолчанию)

c 7 = c 6; // побитовоекопирование

cout << "Edge L ength=" << c7.getEdge L ength() << " Volume=" << c7.getVolume() << " Color=" << c7.getColor() << endl;

// Edge L ength =5.6 Volume =175.616 Color = orange

Можно перегрузить оператор присваивания, чтобы переопределить его значение по умолчанию.

Конструктор копирования вместо оператора присваивания копии используется в объявлении::

cube c 8 = c 6; // Вызывается конструктор копирования, а не оператор //присваивания копий

            // Тоже что иcube c 8(c 6)

Оператор присваивания копии по умолчанию выполняет теневую копию. Он не копирует динамически распределенные элементы данных, созданные с помощью операторов new или new[].

Оператор присваивания копии имеет следующую сигнатуру:

class yClass {

private:

T1 member1;

T2 member2;

public:

// Оператор присваивания копии по умолчанию, который присваивает объект посредством побитовой копии

MyClass & operator=(const MyClass & rhs) {

member1 = rhs.member1;

member2 = rhs.member2;

return *this;

}

......

}

Оператор присваивания копий отличается от конструктора копирования тем, что он должен освобождать динамически распределенное содержимое объекта и предотвращать самоприсваивание. Оператор присваивания возвращает ссылку на этот объект, чтобы разрешить операцию по цепочке(например, x = y = z).

Подводя итог можно сказать. Если в классе не определен конструктор по умолчанию, конструктор копии, оператор присваивания копии и деструктор для типа, то компилятор C ++ создает их автоматически. Эти функции известны как специальные функции-члены; именно благодаря им простые пользовательские типы в С++ действуют подобно структурам в С. Иными словами, их можно создавать, копировать и уничтожать без написания дополнительного кода. В C++11 в язык привносится семантика перемещения: для этого в список специальных функций-членов, которые компилятор может создавать автоматически, добавлены конструктор перемещения, оператор перемещения и присваивания.

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


Поделиться с друзьями:

Опора деревянной одностоечной и способы укрепление угловых опор: Опоры ВЛ - конструкции, предназначен­ные для поддерживания проводов на необходимой высоте над землей, водой...

Двойное оплодотворение у цветковых растений: Оплодотворение - это процесс слияния мужской и женской половых клеток с образованием зиготы...

История развития пистолетов-пулеметов: Предпосылкой для возникновения пистолетов-пулеметов послужила давняя тенденция тяготения винтовок...

Механическое удерживание земляных масс: Механическое удерживание земляных масс на склоне обеспечивают контрфорсными сооружениями различных конструкций...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.011 с.