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