Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
1

Классы и перегрузка операций

28.08.2019, 11:40. Показов 1026. Ответов 20
Метки нет (Все метки)

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

1)У Дейтлов в книге есть пример ими созданного класса Array, для работы с массивами целочисленных значений.
Там есть перегруженная элемент-функция(метод) присваивания одного объекта класса другому(например integer1=integer2):

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const Array &Array::operator=(const Array &right)
{
   if (&right!=this) //избегать самоприсваивания
   {
      if (size!=right.size)
      {
         delete [] ptr;
         size = right.size;
         ptr=new int[size];
      }
      
      for (int i=0;i<size;i++)
      {
         prt[i]=right.ptr[i];
      }
      
      return *this;
   }
}
И ниже есть описание этой функции, где указано, цитирую:

"Если бы проверка на самоприсваивание не производилась, то при самоприсваивании функция operator= перед своим завершением удаляла бы динамическую память, ассоциированную с объектом." В результате ptr указывал бы на память, которая была удалена."

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


2)И второй вопрос:
Есть у меня в классе Str(мною сделанном) функция присваивания одного объекта Str другому:

C++
1
2
3
4
5
6
7
8
9
const Str &Str::operator=(const Str &right)
{
   delete [] sPtr;
   length=right.length;
   sPtr=new char[length+1];
   strcpy(sPtr,right.sPtr);
 
   return *this;
}
Я к примеру присваиваю один объект другому: string2=srting1;

Если я использую вышеуказанную функцию, как написано, всё нормально присваивается.
Но если я пытаюсь сделать возврат из функции не по ссылке а по значению, в string2 хранится мусор.
Помогает только использование Конструктора копирования.

C++
1
2
3
4
5
6
Str::Str(const Str &copys)
{
   length=copys.length;
   sPtr=new char[length+1];
   strcpy(sPtr,copys.sPtr);
}
Объясните пожалуйста, почему в в случае передачи по значению и без использования Конструктора копирования, в
string2 лежит мусор? Ведь все действия по записи в него string1 были произведены в функции. Как на него возврат данных влияет?
0

Помощь в написании контрольных, курсовых и дипломных работ здесь.

Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
28.08.2019, 11:40
Ответы с готовыми решениями:

Классы. Перегрузка операций. С++
Разработайте программу на языке С++, используя пользовательский тип данных – классы и перегрузку...

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

Перегрузка операций << и >>
Всем добрый день.. У меня такая проблема, не получается перегрузить операторы &lt;&lt; и &gt;&gt; для своего...

Перегрузка операций
C++ ругается когда я пытаюсь сделать след-ю перегрузку операций. У меня трехмерный массив matrix и...

20
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 11:49 2
dm_Consul, первое на что обратил внимание? не при всех ветвлениях имеется оператор return.
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
28.08.2019, 11:56  [ТС] 3
Цитата Сообщение от _stanislav Посмотреть сообщение
dm_Consul, первое на что обратил внимание? не при всех ветвлениях имеется оператор return.
Извиняюсь, неправильно написал, в первом вопросе return вне if стоит. А вообще тут везде return используется только для каскадирования.
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 12:06 4
Цитата Сообщение от dm_Consul Посмотреть сообщение
В результате ptr указывал бы на память, которая была удалена
наверно опечатка, тут первый if только для оптимизации, то есть защита от копирования массива самому себе.
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
28.08.2019, 12:49  [ТС] 5
Цитата Сообщение от _stanislav Посмотреть сообщение
наверно опечатка, тут первый if только для оптимизации, то есть защита от копирования массива самому себе.
А подскажите пожалуйста по второму вопросу.
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 13:01 6
Цитата Сообщение от dm_Consul Посмотреть сообщение
Объясните пожалуйста, почему в в случае передачи по значению и без использования Конструктора копирования, в
string2 лежит мусор?
потому что при передаче по значению создается новый объект, с последующим копированием туда данных, при помощи конструктора копирования.
0
221 / 148 / 79
Регистрация: 14.03.2016
Сообщений: 459
28.08.2019, 13:15 7
Цитата Сообщение от dm_Consul Посмотреть сообщение
Объясните пожалуйста, почему в в случае передачи по значению и без использования Конструктора копирования, в
string2 лежит мусор?
Если вы не используете кастомный конструктор копирования, то все данные копируются дефолтным конструктором, т.е. вместо того, чтобы выделить новую память и скопировать туда значения, дефолтный конструктор просто копирует каждое поле из переданного объекта класса в свой. То есть, когда вы в функции копирования operator= возвращаете объект по значению, создается временный объект, скопированный дефолтным конструктором (его sPtr указывает не на свой участок памяти под строку), и так как он временный, почти сразу после выхода из функции он будет уничтожен, будет вызван деструктор, который, полагаю, освободит память, занятой строкой.
1
9 / 7 / 2
Регистрация: 13.11.2014
Сообщений: 51
28.08.2019, 13:38 8
Цитата Сообщение от Cortas Посмотреть сообщение
Если вы не используете кастомный конструктор копирования, то все данные копируются дефолтным конструктором, т.е. вместо того, чтобы выделить новую память и скопировать туда значения, дефолтный конструктор просто копирует каждое поле из переданного объекта класса в свой. То есть, когда вы в функции копирования operator= возвращаете объект по значению, создается временный объект, скопированный дефолтным конструктором (его sPtr указывает не на свой участок памяти под строку), и так как он временный, почти сразу после выхода из функции он будет уничтожен, будет вызван деструктор, который, полагаю, освободит память, занятой строкой.
Присоединяюсь. Пост отражает всю суть происходящего.

Цитата Сообщение от dm_Consul Посмотреть сообщение
А подскажите пожалуйста по второму вопросу.
Приложи весь код класса Str, чтоб развеять сомнения.
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
28.08.2019, 14:15  [ТС] 9
Цитата Сообщение от born4bits Посмотреть сообщение
Приложи весь код класса Str, чтоб развеять сомнения.
Код ниже(с конструктором копии и возвратом по ссылке).
У меня не проблема с написанием, а именно больше с пониманием, почему происходит уничтожение(в данном случае str3),
если убрать конструктор копии и возвращать объект по значению .После поста Cortas, вроде немного понял.
То есть он получается, скопировал весь объект, а указатель оставил на область в дин памяти, где str3 находится?

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include<iostream>
using std::ostream;
using std::cin;
using std::cout;
using std::endl;
using std::fixed;
 
#include<ctime>
 
#include<iomanip>
using std::setw;
 
#include<cstring>
 
class Str
{
   friend ostream &operator<<(ostream &,const Str &);
 
public:
   Str(char * = "");
   Str(const Str&);
   Str operator+(const Str &);
   Str const &operator=(const Str &);
   ~Str();
private:
   char *sPtr;
   int length;
};
 
int main()
{
   Str str1("Red");
   Str str2(" pencil");
   Str str3;
 
   str3=str1+str2;
 
   cout<<str3<<endl;
 
   return 0;
}
 
Str::Str(char *outStr)
{
   cout<<"Constr"<<endl;
   length=strlen(outStr);
   sPtr=new char[length+1];
   strcpy(sPtr,outStr);
}
 
Str::Str(const Str &copys)
{
   length=copys.length;
   sPtr=new char[length+1];
   strcpy(sPtr,copys.sPtr);
}
 
Str::~Str()
{
   cout<<"Destr"<<endl;
   delete [] sPtr;
}
 
ostream &operator<<(ostream &output ,const Str &imp)
{
   output<<imp.sPtr;
   return output;
}
 
Str Str::operator+(const Str &right)
{
   size_t nLength=length+right.length;
   char tempPtr[nLength+1];
   strcpy(tempPtr,sPtr);
   strcpy(tempPtr+length,right.sPtr);
   Str tempStr(tempPtr);
 
   return tempStr;
}
 
const Str &Str::operator=(const Str &right)
{
   delete [] sPtr;
   length=right.length;
   sPtr=new char[length+1];
   strcpy(sPtr,right.sPtr);
 
   return *this;
}
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 14:27 10
Цитата Сообщение от dm_Consul Посмотреть сообщение
У меня не проблема с написанием, а именно больше с пониманием
у тебя если что тут не конструктор копии вызывается а operator=:
C++
1
str3=str1+str2;
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
28.08.2019, 14:48  [ТС] 11
Цитата Сообщение от _stanislav Посмотреть сообщение
у тебя если что тут не конструктор копии вызывается а operator=:
C++Выделить код
1
str3=str1+str2;
Это понятно. Я имею ввиду тот момент,когда в operator= происходит возврат return *this.
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 15:03 12
Цитата Сообщение от dm_Consul Посмотреть сообщение
почему происходит уничтожение(в данном случае str3)
вызова деструктора str3 у тебя не происходит (если ты об этом). Деструкторы вызываются:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
Str Str::operator+(const Str &right)
{
   size_t nLength=length+right.length;
   char tempPtr[nLength+1];
   strcpy(tempPtr,sPtr);
   strcpy(tempPtr+length,right.sPtr);
   Str tempStr(tempPtr); // Первый у этого объекта
 
   return tempStr;
}
...
 
str1+str2; // Второй тут, который был создан при возврате из operator+
Добавлено через 1 минуту
можно оптимизировать так:
C++
1
2
3
4
5
6
7
8
9
Str Str::operator+(const Str &right)
{
   size_t nLength=length+right.length;
   char tempPtr[256];
   strcpy(tempPtr,sPtr);
   strcpy(tempPtr+length,right.sPtr);
 
   return tempPtr;
}
Добавлено через 1 минуту
Цитата Сообщение от _stanislav Посмотреть сообщение
char tempPtr[nLength+1];
кстати тут ошибка, размер массива должен быть константой: char tempPtr[256];
0
9 / 7 / 2
Регистрация: 13.11.2014
Сообщений: 51
28.08.2019, 15:03 13
Цитата Сообщение от dm_Consul Посмотреть сообщение
То есть он получается, скопировал весь объект, а указатель оставил на область в дин памяти, где str3 находится?
Да, примерно так.

Поясню немного, когда ты возвращаешь по значению, вот такой вариант:

C++
1
2
3
4
5
6
7
8
9
const Str Str::operator=(const Str &right)
{
   delete [] sPtr;
   length=right.length;
   sPtr=new char[length+1];
   strcpy(sPtr,right.sPtr);
 
   return *this;
}
то происходит следующее - после выхода из оператора присваивания в памяти создается объект класса Str ( ты его не видишь по имени, он невидим, всё это есть в документации по c++). Создается он через конструктор копирования, а именно invisible(str3) (в случае, как в коде main(), invisible чисто мое название, для понимания). Если ты не определил свой конструктор копирования(КК), то будет вызван КК по умолчанию. Он просто тупо копирует поля одного объекта в другой. В твоем случае происходит копирование sPtr и length из str3 в тот самый "invisible" (который скрыт от глаз разработчика). В итоге получается, что в "invisible" продублированы все поля из str3 (в том числе и указатель). И так как данный оператор отработан (=), этот объект больше не нужен (документация c++) и он удаляется (странно звучит, но так оно работает). В деструкторе у тебя стоит "delete [] sptr". Объект invisible удаляет память по указателю, сдублированному с str3, поэтому в st3 останется мусор. При определении своего умного КК, ты вместо простого копирования выделяешь новую память для "invisible", и удаляешь ее, которая принадлежит только ему и точка.

Теперь про оптимизацию. Оба варианта работают

1) передача по ссылке
2) передача по значению, с определением конструктора

Но смотри что во втором случае: ты выделяешь память, никак ее не используешь, а затем удаляешь - простым языком, куча лишних действий. Да, с++ стандарт не заставляет тебя возвращать именно ссылку в операторе присваивания, но принято вообще именно так. Для закрепления понимания советую обратиться к книгам Страуструпа, возможно я где-то соврал и будет не лишним ещё раз понять механизм вызова конструкторов, описанную создателем языка.
1
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 15:05 14
Цитата Сообщение от dm_Consul Посмотреть сообщение
Str(char * = "");
тут тоже нужен const: Str(const char * = "");
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
28.08.2019, 15:22  [ТС] 15
Цитата Сообщение от _stanislav Посмотреть сообщение
кстати тут ошибка, размер массива должен быть константой: char tempPtr[256];
Странно, IDE все нормально отрабатывает.

Цитата Сообщение от born4bits Посмотреть сообщение
Да, примерно так.
Поясню немного
Спасибо большое. Крайне доходчиво.
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
28.08.2019, 15:36 16
Цитата Сообщение от dm_Consul Посмотреть сообщение
Странно, IDE все нормально отрабатывает.
не стандартные расширения IDE, плодят не переносимый код.

Добавлено через 11 минут
Цитата Сообщение от dm_Consul Посмотреть сообщение
Спасибо большое. Крайне доходчиво.
ты наверное уже созрел для понимания таких понятий как rvalue и lvalue, начинай гуглежку, но пока вот пара ссылок:
http://rsdn.org/article/cpp/lvalue.xml
https://habr.com/ru/post/348198/
и конечно же желаю тебе прекрасно повеселиться, когда дойдешь до xvalue, glvalue, prvalue.
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
28.08.2019, 15:54  [ТС] 17
Цитата Сообщение от _stanislav Посмотреть сообщение
ты наверное уже созрел для понимания таких понятий как rvalue и lvalue, начинай гуглежку, но пока вот пара ссылок:
http://rsdn.org/article/cpp/lvalue.xml
https://habr.com/ru/post/348198/
и конечно же желаю тебе прекрасно повеселиться, когда дойдешь до xvalue, glvalue, prvalue.
Чуть чуть их уже понимаю Спасибо почитаю.

А по поводу размера массива, просто Дейтлы в new применяют такой вариант при создании массива в динамической памяти.
Я на автомате и для статического так сделал)
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
29.08.2019, 15:29  [ТС] 18
Цитата Сообщение от _stanislav Посмотреть сообщение
ты наверное уже созрел для понимания таких понятий как rvalue и lvalue
Добрый день.
А вы не могли бы еще подсказать такой момент.
Что возвращает функция если return не указан, а в прототипе и описании функции возвращаемое значение ostream&?
Просто писал класс, забыл return output(синоним cout) в перегруженном << указать,
а каскадный вывод cout<<class1<<class2 все равно сработал.
0
Нарушитель
1486 / 1288 / 485
Регистрация: 16.08.2014
Сообщений: 5,415
Записей в блоге: 1
29.08.2019, 15:46 19
Цитата Сообщение от dm_Consul Посмотреть сообщение
Что возвращает функция если return не указан
неопределенное поведение
0
0 / 0 / 0
Регистрация: 15.05.2019
Сообщений: 71
29.08.2019, 15:58  [ТС] 20
Цитата Сообщение от _stanislav Посмотреть сообщение
неопределенное поведение
а как он объект class2 выводит? Компилятор сам додумывает что там по логике стоять должно?
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
29.08.2019, 15:58

Перегрузка операций
#include &lt;iostream&gt; using namespace std; class chislo { public: chislo(); chislo(int...

Перегрузка операций << и >>
Помогите пожалуйста перегрузить &gt;&gt; и &lt;&lt; #include &lt;fstream&gt; #include &lt;iostream&gt; #include...

Перегрузка операций
Разработайте программу на языке С++, используя пользовательский тип данных – классы и перегрузку...

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.