seminar2
This commit is contained in:
		
							parent
							
								
									46d1c64684
								
							
						
					
					
						commit
						ab6732eded
					
				
					 98 changed files with 10319 additions and 0 deletions
				
			
		|  | @ -0,0 +1,197 @@ | |||
| /*
 | ||||
|     Особые методы | ||||
| 
 | ||||
|     В прошлой части мы рассмотрели различные методы классов. Некоторые из этих методов называются особыми. | ||||
|     Они выделяются по сравнению с другими методами тем, что они будут создаваться автоматически даже если вы их не напишите. | ||||
|     К особым методам класса относятся: | ||||
| 
 | ||||
| 
 | ||||
|         1)  Конструктор по умолчанию. То есть конструктор, который не принимает ни одного аргумента. | ||||
|             В случае со строкой String это контруктор: | ||||
| 
 | ||||
|                 String() | ||||
| 
 | ||||
| 
 | ||||
|         2)  Конструктор копирования. То есть конструктор, который создаёт объект из другого объекта такого же типа, что и данный. | ||||
|             В случае со строкой String это контруктор: | ||||
| 
 | ||||
|                 String(const String& s)  | ||||
| 
 | ||||
| 
 | ||||
|         3)  Деструктор. Это метод, который вызывается при удалении объекта. В отличии от конструкторов, деструктор у объекта всегда один. | ||||
|             В случае со строкой String это: | ||||
| 
 | ||||
|                 ~String() | ||||
| 
 | ||||
|          | ||||
|         4)  Оператор присваивания. Это метод, который вызывается при присваивании одного метода класса String другому методу класса String. | ||||
|             В случае со строкой String это: | ||||
| 
 | ||||
|                 String& operator=(const String& right) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         5,6)    Есть ещё 2 особых метода, которые мы пройдём позже. | ||||
|      | ||||
| 
 | ||||
|     Все остальные методы, включаю другие конструкторы и перегруженные операторы, к особым методам не относятся. | ||||
|     То есть остальные методы не могут создаваться автоматически. | ||||
| 
 | ||||
| 
 | ||||
|     В этой части мы рассмотрим при каких условиях происходит вызов того или иного особого метода. | ||||
|     Для этого будем использовать класс String, в котором была добавлена печать на экран для каждого особого метода и для коструктора из const char*. | ||||
|     Например, конструктор по умолчанию будет печатать Default Constructor и т. д. | ||||
| */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| using std::cout, std::endl, std::size_t; | ||||
| 
 | ||||
| 
 | ||||
| class String  | ||||
| { | ||||
| private: | ||||
| 
 | ||||
|     size_t mSize; | ||||
|     size_t mCapacity; | ||||
|     char* mpData; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     String(const char* str)  | ||||
|     { | ||||
|         cout << "String Constructor from const char*  (" << str << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         while (str[i] != '\0') | ||||
|             i++; | ||||
|         mSize = i; | ||||
|         mCapacity = i + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; str[i]; ++i) | ||||
|             mpData[i] = str[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String() | ||||
|     { | ||||
|         cout << "String Default Constructor" << endl; | ||||
|         mSize = 0; | ||||
|         mCapacity = 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char)); | ||||
|         mpData[0] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String(const String& s)  | ||||
|     { | ||||
|         cout << "String Copy Constructor  (" << s.mpData << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         mSize = s.mSize; | ||||
|         mCapacity = mSize + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = s.mpData[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     ~String() | ||||
|     { | ||||
|         cout << "String Destructor  (" << mpData << ")" << endl; | ||||
|         std::free(mpData); | ||||
|     } | ||||
| 
 | ||||
|     String& operator=(const String& right) | ||||
|     { | ||||
|         cout << "String Assignment Operator  (" << right.mpData << ")" << endl; | ||||
|         if (this == &right) | ||||
|             return *this; | ||||
| 
 | ||||
| 
 | ||||
|         mSize = right.mSize; | ||||
|         mCapacity = right.mCapacity; | ||||
| 
 | ||||
|         std::free(mpData); | ||||
|         mpData = (char*)malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i <= mSize; ++i) | ||||
|             mpData[i] = right.mpData[i]; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     char& operator[](size_t i) | ||||
|     { | ||||
|         return mpData[i]; | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize()        const   {return mSize;} | ||||
|     size_t getCapacity()    const   {return mCapacity;} | ||||
|     const char* cStr()      const   {return mpData;} | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& left, const String& right)  | ||||
| { | ||||
|     left << right.cStr(); | ||||
|     return left; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Book | ||||
| { | ||||
| public: | ||||
|     String title; | ||||
|     int pages; | ||||
|     float price; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int main()  | ||||
| { | ||||
|     Book a = Book(); | ||||
|     cout << a.title << " " << a.pages << " " << a.price << endl; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|     У класса Book есть 3 поля, одно из которых имеет тип String. | ||||
|     Но у класса Book не определён ни один метод. У него нет никаких конструкторов или перегруженных операторов. | ||||
| 
 | ||||
|     Теперь посмотрим на код: | ||||
| 
 | ||||
|         Book a = Book(); | ||||
| 
 | ||||
|     Тут мы создаём объект типа Book с помощью конструктора по умолчанию. Но у Book не написан конструктор по умолчанию! | ||||
|     Что же произойдёт в этом случае? Ошибка? Нет, на самом деле в этом случае конструктор будет создан автоматически. Вот такой: | ||||
| 
 | ||||
|         Book() {} | ||||
| 
 | ||||
|     Автоматически-сгенерированный конструктор по умолчанию ничего не делает. | ||||
|     Тем не менее, перед вызовом этого конструктора, компилятор должен вызвать конструктор по умолчанию для всех полей класса,  | ||||
|     у которых есть конструктор по умолчанию. | ||||
|     В этом случае, при создании объекта класса Book с помощью конструктора по умолчанию, будет вызван конструктор по умолчанию класса String. | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|     Аналогично, автоматически сгенерируется деструктор по умолчанию, вот такой: | ||||
| 
 | ||||
|         ~Book() {} | ||||
| 
 | ||||
|     Он пустой, но нужно помнить, что после вызова деструктора класса, автоматически вызываются деструкторы для все его полей (у которых есть деструкторы) | ||||
|     В этом случае, после вызова деструктора класса Book вызовется деструктор класса String. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     Задачи: | ||||
| 
 | ||||
|         1)  Скомпилируйте программу, запустите и убедитесь, что в этом примере вызываются  | ||||
|             конструктор по умолчанию и деструктор класса String. | ||||
| */ | ||||
|  | @ -0,0 +1,191 @@ | |||
| /*
 | ||||
|     Особые методы | ||||
| 
 | ||||
|     Проверим, что компилятор автоматически генерирует конструктор копирования и оператор присваивания. | ||||
| */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| using std::cout, std::endl, std::size_t; | ||||
| 
 | ||||
| 
 | ||||
| class String  | ||||
| { | ||||
| private: | ||||
| 
 | ||||
|     size_t mSize; | ||||
|     size_t mCapacity; | ||||
|     char* mpData; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     String(const char* str)  | ||||
|     { | ||||
|         cout << "String Constructor from const char*  (" << str << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         while (str[i] != '\0') | ||||
|             i++; | ||||
|         mSize = i; | ||||
|         mCapacity = i + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; str[i]; ++i) | ||||
|             mpData[i] = str[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String() | ||||
|     { | ||||
|         cout << "String Default Constructor" << endl; | ||||
|         mSize = 0; | ||||
|         mCapacity = 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char)); | ||||
|         mpData[0] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String(const String& s)  | ||||
|     { | ||||
|         cout << "String Copy Constructor  (" << s.mpData << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         mSize = s.mSize; | ||||
|         mCapacity = mSize + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = s.mpData[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     ~String() | ||||
|     { | ||||
|         cout << "String Destructor  (" << mpData << ")" << endl; | ||||
|         std::free(mpData); | ||||
|     } | ||||
| 
 | ||||
|     String& operator=(const String& right) | ||||
|     { | ||||
|         cout << "String Assignment Operator  (" << right.mpData << ")" << endl; | ||||
|         if (this == &right) | ||||
|             return *this; | ||||
| 
 | ||||
| 
 | ||||
|         mSize = right.mSize; | ||||
|         mCapacity = right.mCapacity; | ||||
| 
 | ||||
|         std::free(mpData); | ||||
|         mpData = (char*)malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i <= mSize; ++i) | ||||
|             mpData[i] = right.mpData[i]; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     char& operator[](size_t i) | ||||
|     { | ||||
|         return mpData[i]; | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize()        const   {return mSize;} | ||||
|     size_t getCapacity()    const   {return mCapacity;} | ||||
|     const char* cStr()      const   {return mpData;} | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& left, const String& right)  | ||||
| { | ||||
|     left << right.cStr(); | ||||
|     return left; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Book | ||||
| { | ||||
| public: | ||||
|     String title; | ||||
|     int pages; | ||||
|     float price; | ||||
| }; | ||||
| 
 | ||||
| void print(Book x) | ||||
| { | ||||
|     cout << x.title << " " << x.pages << " " << x.price << endl; | ||||
| } | ||||
| 
 | ||||
| int main()  | ||||
| { | ||||
|     Book a; | ||||
| 
 | ||||
|     a.title = "War and Peace"; | ||||
|     a.pages = 1000; | ||||
|     a.price = 1100; | ||||
| 
 | ||||
|     Book b; | ||||
|     b = a; | ||||
|     print(b); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|     Что напечатает данная программа? | ||||
| 
 | ||||
|     Разберём код из функции main подробно: | ||||
| 
 | ||||
|         1)  Вызывается автоматически сгенерированный конструктор по умолчанию в строке:  | ||||
| 
 | ||||
|                 Book a; | ||||
| 
 | ||||
|             При этом перед вызовом этого конструктора вызовется и конструктор класса String поля title. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         2)  Полям объекта a присваиваются некоторые значения: | ||||
| 
 | ||||
|                 a.title = "War and Peace"; | ||||
|                 a.pages = 1000; | ||||
|                 a.price = 1100; | ||||
| 
 | ||||
|             Интересно отметить, что строка  a.title = "War and Peace"  работает, несмотря на то, что слева от знака  | ||||
|             присваивания стоит объект типа String, а справа от знака присваивания стоит объект типа  const char[14]. | ||||
|             Типы не совпадают, но это работает, так как у класса String есть конструктор от const char*. | ||||
|             Таким образом, сначала вызовется этот конструктор и создастся временный объект типа String, а потом | ||||
|             вызовется оператор присваивания между двумя объектами класса String.  | ||||
|             После этого временный объект удалится и, соответственно, вызовется его деструктор. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         3)  Вызывается автоматически сгенерированный конструктор по умолчанию в строке: | ||||
| 
 | ||||
|                 Book b; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         4)  Вызывается автоматически сгенерированный перегруженный оператор присваивания в строке: | ||||
| 
 | ||||
|                 b = a; | ||||
| 
 | ||||
|             Автоматически сгенерированный оператор присваивания применяет оператор присваивания для каждого поля. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         5)  Передача в функцию  print  осуществляется по значению. Следовательно в строке | ||||
| 
 | ||||
|                 print(b); | ||||
| 
 | ||||
|             объект  b  должен быть скопирован внутрь функции. Для этого должен быть вызван конструктор копирования. | ||||
|             Конструктор копирования у класса Book не написан, поэтому компилятор автоматически его сгенерирует. | ||||
| 
 | ||||
|             Автоматически сгенерированный конструктор копирования копирует каждое поле в соответствующее поле нового объекта. | ||||
|             Если у поля есть конструктор копирования (как например у класса String), то вызывается конструктор копирования этого поля. | ||||
|             Если у поля нет конструктора копирования (как например у int или float), то поле просто копируется побайтово. | ||||
| 
 | ||||
| 
 | ||||
|         6)  Выходим из функции print и вызываем деструктор для объекта x. | ||||
| 
 | ||||
|         7)  Выходим из функции main и вызываем деструкторы для объектов a и b. | ||||
| */ | ||||
|  | @ -0,0 +1,148 @@ | |||
| /*
 | ||||
|     Замечание по поводу автоматической генерации конструктора по умолчанию | ||||
| */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| using std::cout, std::endl, std::size_t; | ||||
| 
 | ||||
| 
 | ||||
| class String  | ||||
| { | ||||
| private: | ||||
| 
 | ||||
|     size_t mSize; | ||||
|     size_t mCapacity; | ||||
|     char* mpData; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     String(const char* str)  | ||||
|     { | ||||
|         cout << "String Constructor from const char*  (" << str << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         while (str[i] != '\0') | ||||
|             i++; | ||||
|         mSize = i; | ||||
|         mCapacity = i + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; str[i]; ++i) | ||||
|             mpData[i] = str[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String() | ||||
|     { | ||||
|         cout << "String Default Constructor" << endl; | ||||
|         mSize = 0; | ||||
|         mCapacity = 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char)); | ||||
|         mpData[0] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String(const String& s)  | ||||
|     { | ||||
|         cout << "String Copy Constructor  (" << s.mpData << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         mSize = s.mSize; | ||||
|         mCapacity = mSize + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = s.mpData[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     ~String() | ||||
|     { | ||||
|         cout << "String Destructor  (" << mpData << ")" << endl; | ||||
|         std::free(mpData); | ||||
|     } | ||||
| 
 | ||||
|     String& operator=(const String& right) | ||||
|     { | ||||
|         cout << "String Assignment Operator  (" << right.mpData << ")" << endl; | ||||
|         if (this == &right) | ||||
|             return *this; | ||||
| 
 | ||||
| 
 | ||||
|         mSize = right.mSize; | ||||
|         mCapacity = right.mCapacity; | ||||
| 
 | ||||
|         std::free(mpData); | ||||
|         mpData = (char*)malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i <= mSize; ++i) | ||||
|             mpData[i] = right.mpData[i]; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     char& operator[](size_t i) | ||||
|     { | ||||
|         return mpData[i]; | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize()        const   {return mSize;} | ||||
|     size_t getCapacity()    const   {return mCapacity;} | ||||
|     const char* cStr()      const   {return mpData;} | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& left, const String& right)  | ||||
| { | ||||
|     left << right.cStr(); | ||||
|     return left; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Book | ||||
| { | ||||
| public: | ||||
|     String title; | ||||
|     int pages; | ||||
|     float price; | ||||
| 
 | ||||
|     Book(const String& aTitle, int aPages, float aPrice) | ||||
|     { | ||||
|         title = aTitle; | ||||
|         pages = aPages; | ||||
|         price = aPrice; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int main()  | ||||
| { | ||||
|     Book a; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|     Конструктор по умолчанию не генерируется автоматически если у класса написан хотя бы один конструктор (любой). | ||||
|     Например, в этом пример у класса Book написан один конструктор: | ||||
| 
 | ||||
|         Book(const String& aTitle, int aPages, float aPrice) | ||||
| 
 | ||||
|     Поэтому в данном случае конструктор по умочанию автоматически генерироваться не будет. | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
|     В строке  Book a;  должен быть вызван конструктор по умолчанию. | ||||
|     Но у класса Book такого конструктора нет и автоматически он не был создан. Поэтому эта строка приведёт к ошибке. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     Но конструктор копирования, оператор присваивания и деструктор автоматически генерироваться будут. | ||||
| 
 | ||||
| 
 | ||||
|     Задача: | ||||
| 
 | ||||
|         Исправьте ошибку, написав конструктор по умолчанию самостоятельно. | ||||
| */ | ||||
|  | @ -0,0 +1,164 @@ | |||
| /*
 | ||||
|     Удалённые методы | ||||
| */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| using std::cout, std::endl, std::size_t; | ||||
| 
 | ||||
| 
 | ||||
| class String  | ||||
| { | ||||
| private: | ||||
| 
 | ||||
|     size_t mSize; | ||||
|     size_t mCapacity; | ||||
|     char* mpData; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     String(const char* str)  | ||||
|     { | ||||
|         cout << "String Constructor from const char*  (" << str << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         while (str[i] != '\0') | ||||
|             i++; | ||||
|         mSize = i; | ||||
|         mCapacity = i + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; str[i]; ++i) | ||||
|             mpData[i] = str[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String() | ||||
|     { | ||||
|         cout << "String Default Constructor" << endl; | ||||
|         mSize = 0; | ||||
|         mCapacity = 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char)); | ||||
|         mpData[0] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String(const String& s)  | ||||
|     { | ||||
|         cout << "String Copy Constructor  (" << s.mpData << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         mSize = s.mSize; | ||||
|         mCapacity = mSize + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = s.mpData[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     ~String() | ||||
|     { | ||||
|         cout << "String Destructor  (" << mpData << ")" << endl; | ||||
|         std::free(mpData); | ||||
|     } | ||||
| 
 | ||||
|     String& operator=(const String& right) | ||||
|     { | ||||
|         cout << "String Assignment Operator  (" << right.mpData << ")" << endl; | ||||
|         if (this == &right) | ||||
|             return *this; | ||||
| 
 | ||||
| 
 | ||||
|         mSize = right.mSize; | ||||
|         mCapacity = right.mCapacity; | ||||
| 
 | ||||
|         std::free(mpData); | ||||
|         mpData = (char*)malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i <= mSize; ++i) | ||||
|             mpData[i] = right.mpData[i]; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     char& operator[](size_t i) | ||||
|     { | ||||
|         return mpData[i]; | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize()        const   {return mSize;} | ||||
|     size_t getCapacity()    const   {return mCapacity;} | ||||
|     const char* cStr()      const   {return mpData;} | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& left, const String& right)  | ||||
| { | ||||
|     left << right.cStr(); | ||||
|     return left; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class Book | ||||
| { | ||||
| public: | ||||
|     String title; | ||||
|     int pages; | ||||
|     float price; | ||||
| 
 | ||||
|     Book() {}; | ||||
| 
 | ||||
|     Book(const String& aTitle, int aPages, float aPrice) | ||||
|     { | ||||
|         title = aTitle; | ||||
|         pages = aPages; | ||||
|         price = aPrice; | ||||
|     } | ||||
| 
 | ||||
|     Book(const Book& b) = delete; | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int main()  | ||||
| { | ||||
|     Book a; | ||||
| 
 | ||||
|     Book b = a; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|     Если же вы не хотите создавать какой-либо метод и не хотите чтобы он создавался автоматически,  | ||||
|     то его можно удалить с помощью ключевого слова delete. | ||||
| 
 | ||||
|      | ||||
|     Например, в этом примере у класса Book удалён конструктор копирования вот так: | ||||
| 
 | ||||
|         Book(const Book& b) = delete; | ||||
| 
 | ||||
|     Это означает, что конструктор копирования не создастся автоматически. | ||||
|     Поэтому в строке: | ||||
| 
 | ||||
|         Book b = a; | ||||
| 
 | ||||
|     произойдёт ошибка компиляции. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     Задача: | ||||
| 
 | ||||
|         1)  Можно ли передать объект класса с удалённым конструктором копирования в функцию по значению? | ||||
|              | ||||
|             Например, если есть функция: | ||||
| 
 | ||||
|                 void print(Book b) | ||||
|                 { | ||||
|                     cout << b.title << " "<< b.pages << " " << b.price << endl; | ||||
|                 } | ||||
| 
 | ||||
|             Можно ли туда передать что-нибудь? | ||||
| 
 | ||||
| */ | ||||
|  | @ -0,0 +1,188 @@ | |||
| /*
 | ||||
|     Неявное приведение типа с помощью конструктора от одного параметра. | ||||
| */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| using std::cout, std::endl, std::size_t; | ||||
| 
 | ||||
| 
 | ||||
| class String  | ||||
| { | ||||
| private: | ||||
| 
 | ||||
|     size_t mSize; | ||||
|     size_t mCapacity; | ||||
|     char* mpData; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     String(const char* s)  | ||||
|     { | ||||
|         cout << "String Constructor from const char*  (" << s << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         while (s[i] != '\0') | ||||
|             i++; | ||||
|         mSize = i; | ||||
|         mCapacity = i + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; s[i]; ++i) | ||||
|             mpData[i] = s[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String() | ||||
|     { | ||||
|         cout << "String Default Constructor" << endl; | ||||
|         mSize = 0; | ||||
|         mCapacity = 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char)); | ||||
|         mpData[0] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String(const String& s)  | ||||
|     { | ||||
|         cout << "String Copy Constructor  (" << s.mpData << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         mSize = s.mSize; | ||||
|         mCapacity = mSize + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = s.mpData[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     ~String() | ||||
|     { | ||||
|         cout << "String Destructor  (" << mpData << ")" << endl; | ||||
|         std::free(mpData); | ||||
|     } | ||||
| 
 | ||||
|     String& operator=(const String& right) | ||||
|     { | ||||
|         cout << "String Assignment Operator  (" << right.mpData << ")" << endl; | ||||
|         if (this == &right) | ||||
|             return *this; | ||||
| 
 | ||||
| 
 | ||||
|         mSize = right.mSize; | ||||
|         mCapacity = right.mCapacity; | ||||
| 
 | ||||
|         std::free(mpData); | ||||
|         mpData = (char*)malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i <= mSize; ++i) | ||||
|             mpData[i] = right.mpData[i]; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     char& operator[](size_t i) | ||||
|     { | ||||
|         return mpData[i]; | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize()        const   {return mSize;} | ||||
|     size_t getCapacity()    const   {return mCapacity;} | ||||
|     const char* cStr()      const   {return mpData;} | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& left, const String& right)  | ||||
| { | ||||
|     left << right.cStr(); | ||||
|     return left; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void print(String s) | ||||
| { | ||||
|     cout << s << endl; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int main()  | ||||
| { | ||||
|     String a; | ||||
|     a = "Dog"; | ||||
| 
 | ||||
|     print("Mouse"); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|     Одна из скрытых вещей, где используются конструкторы, это для неявного приведения типов. | ||||
| 
 | ||||
|     Рассмотрим следующие строки: | ||||
| 
 | ||||
|         String a; | ||||
|         a = "Dog"; | ||||
| 
 | ||||
|     Во второй строке должен вызваться оператор присваивания.  | ||||
|     Слева от оператора присваивания стоит объект типа String, а справа от оператора присваивания стоит объект типа  const char[4]. | ||||
|     Поэтому должен вызваться метод  operator=(const char* s) класса String. (при передаче в функцию массив автоматически конвертируется в указатель) | ||||
|     Но такого метода в классе String нет. Что же тогда будет сделано? | ||||
| 
 | ||||
|     В этом случае будет сделано следующее: | ||||
| 
 | ||||
|         1)  Будет создан временный объект типа String с использованием конструтора  String(const char* s). | ||||
|         2)  Будет вызван оператор присваивания operator=(const String& s). Объекту a присвоится временный объект. | ||||
|         3)  Временный объект будет уничтожен, при этом вызовется деструктор класса String. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     Рассмотрим строку: | ||||
| 
 | ||||
|         print("Mouse"); | ||||
| 
 | ||||
|     Функция print должна принимать объект типа String, но на вход ей приходит объект типа  const char[6]. | ||||
|     Что будет сделано в этом случае? | ||||
|     И в этом случае всё сработает, так как объект  s  функции print будет создан с помощью конструктора класса String от const char*. | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|     Таким образом конструктор от одного аргумента автоматически используется для неявного приведения одного типа в другой. | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     Задачи: | ||||
| 
 | ||||
|         1)  Что если мы напишем конструктор класса String от числа типа int. | ||||
|             Этот конструктор будет принимать число n и создавать строку, состоящую из n символов 'a' | ||||
| 
 | ||||
| 
 | ||||
|                 String(int n) | ||||
|                 { | ||||
|                     cout << "String Constructor from int  (" << n << ")" << endl; | ||||
| 
 | ||||
|                     mSize = n; | ||||
|                     mCapacity = mSize + 1; | ||||
|                     mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
|                     for (size_t i = 0; i < mSize; ++i) | ||||
|                         mpData[i] = 'a'; | ||||
|                     mpData[mSize] = '\0'; | ||||
|                 } | ||||
| 
 | ||||
|          | ||||
|              | ||||
|             Будет ли этот конструктор использоваться для неявного приведения чисел типа int в строки типа String? | ||||
|             Например, будет ли работать следующий код: | ||||
| 
 | ||||
|                 String b; | ||||
|                 b = 5; | ||||
| 
 | ||||
|             (b будет строкой состоящей из 5 символов 'a' ?) | ||||
|      | ||||
| 
 | ||||
|                 print(10); | ||||
| 
 | ||||
|             (напечатает строку, состоящую из 10 символов 'a') | ||||
| */ | ||||
|  | @ -0,0 +1,147 @@ | |||
| /*
 | ||||
|     Ключевое слово explicit. | ||||
| */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <cstdlib> | ||||
| using std::cout, std::endl, std::size_t; | ||||
| 
 | ||||
| 
 | ||||
| class String  | ||||
| { | ||||
| private: | ||||
| 
 | ||||
|     size_t mSize; | ||||
|     size_t mCapacity; | ||||
|     char* mpData; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|     explicit String(const char* s)  | ||||
|     { | ||||
|         cout << "String Constructor from const char*  (" << s << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         while (s[i] != '\0') | ||||
|             i++; | ||||
|         mSize = i; | ||||
|         mCapacity = i + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; s[i]; ++i) | ||||
|             mpData[i] = s[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     explicit String(int n) | ||||
|     { | ||||
|         cout << "String Constructor from int  (" << n << ")" << endl; | ||||
| 
 | ||||
|         mSize = n; | ||||
|         mCapacity = mSize + 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = 'a'; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String() | ||||
|     { | ||||
|         cout << "String Default Constructor" << endl; | ||||
|         mSize = 0; | ||||
|         mCapacity = 1; | ||||
|         mpData = (char*)std::malloc(sizeof(char)); | ||||
|         mpData[0] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     String(const String& s)  | ||||
|     { | ||||
|         cout << "String Copy Constructor  (" << s.mpData << ")" << endl; | ||||
| 
 | ||||
|         size_t i = 0; | ||||
|         mSize = s.mSize; | ||||
|         mCapacity = mSize + 1; | ||||
| 
 | ||||
|         mpData = (char*)std::malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i < mSize; ++i) | ||||
|             mpData[i] = s.mpData[i]; | ||||
|         mpData[mSize] = '\0'; | ||||
|     } | ||||
| 
 | ||||
|     ~String() | ||||
|     { | ||||
|         cout << "String Destructor  (" << mpData << ")" << endl; | ||||
|         std::free(mpData); | ||||
|     } | ||||
| 
 | ||||
|     String& operator=(const String& right) | ||||
|     { | ||||
|         cout << "String Assignment Operator  (" << right.mpData << ")" << endl; | ||||
|         if (this == &right) | ||||
|             return *this; | ||||
| 
 | ||||
| 
 | ||||
|         mSize = right.mSize; | ||||
|         mCapacity = right.mCapacity; | ||||
| 
 | ||||
|         std::free(mpData); | ||||
|         mpData = (char*)malloc(sizeof(char) * mCapacity); | ||||
| 
 | ||||
|         for (size_t i = 0; i <= mSize; ++i) | ||||
|             mpData[i] = right.mpData[i]; | ||||
| 
 | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     char& operator[](size_t i) | ||||
|     { | ||||
|         return mpData[i]; | ||||
|     } | ||||
| 
 | ||||
|     size_t getSize()        const   {return mSize;} | ||||
|     size_t getCapacity()    const   {return mCapacity;} | ||||
|     const char* cStr()      const   {return mpData;} | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& left, const String& right)  | ||||
| { | ||||
|     left << right.cStr(); | ||||
|     return left; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void print(String s) | ||||
| { | ||||
|     cout << s << endl; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| int main()  | ||||
| { | ||||
|     String a; | ||||
|     a = "Dog"; | ||||
| 
 | ||||
|     print("Mouse"); | ||||
|     print(10); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|     Иногда всё-таки не хочется, чтобы конструкторы использовались для неявного приведения типов. | ||||
|     Ведь такое приведение типов может произойти там, где мы этого не хотим. | ||||
| 
 | ||||
|     Чтобы конструктор не использовался для неявного приведения, его нужно пометить с помощью ключевого слова explicit. | ||||
|     В этом примере, конструкторы   String(const char* s)  и  String(int n)  помечены как explicit. | ||||
|     Поэтому эти конструкторы не будут использоваться для неявного приведения типов и код в функции main выдаст ошибку. | ||||
|     Но эти конструкторы всё равно можно вызывать явно, вот так: | ||||
| 
 | ||||
|         String a; | ||||
|         a = String("Dog"); | ||||
| 
 | ||||
|         print(String("Mouse")); | ||||
|         print(String(10)); | ||||
| */ | ||||
		Reference in a new issue