Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
#1

Особенности использования указателей и ссылок в C++ при возврате из функции - C++

04.08.2013, 11:59. Просмотров 920. Ответов 16
Метки нет (Все метки)

Пусть у нас есть некий класс CBase и есть функция, которая создает и возвращает объект класса CBase. Создать она его может стеке или в куче, а вернуть может сам объект, ссылку на него или указатель… Чем же различаются эти варианты? Для ответа напишем простой тест, использующий разные варианты и сравним.
Кликните здесь для просмотра всего текста

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
class CBase
{
public:
    int i;
    // конструктор по-умолчанию
    CBase()     
    {  
        i=0;
        name = NULL;  
        cout<<"Base() "<<endl;
    }
 
    CBase(const char *str1)     
    {  
        i=1;
        name = new char[strlen(str1)+1];  
        strcpy(name, str1); 
        cout<<"Base "<<i<<" "<<name<<endl;
    }
    // конструктор копирования, создает новую строку с добавлением @ в начале
    CBase(CBase &b)     
    {  
        i=2;
        name = new char[strlen(b.name)+2];  
        name[0]='@';
        strcpy(name+1, b.name); 
        cout<<"CopyBase "<<i<<" "<<name<<endl;
    }
    CBase & operator = (const CBase& b)
    {   i=3;
        cout<<"Base = "<<i<<" "<<name<<endl;
        return *this;
    }
    virtual ~CBase()                          
    { 
        cout<<"~Base "<<i<<" "<<name<<endl;
        delete [] name;
    }
    void SetString(const char *str1) {
        delete [] name;
        name = new char[strlen(str1)+1];  
        strcpy(name, str1); 
    }
protected:
    char *name;
};
 
class CDerived : public CBase
{
public:
    CDerived(const char *str1, const char *str2) 
        : CBase(str1)   
    { 
        name2 = new char[strlen(str2)+1];  
        strcpy(name2, str2);
        cout<<"Derived "<<name2<<endl;
    }
      ~CDerived()                                             
    {  
//      CBase::~CBase(); // а вот этого - не надо
        delete [] name2;
        cout<<"~Derived "<<name2<<endl;
    }
protected:
    char *name2;
};
 
// функции именуются так: get<символ возврата><символ создания>
//символ создания - как новый объект создается в процедуре:
// p - через new (p-указатель на созданный объект)
// o - через локальную переменную (o- локальная переменная)
//символ возврата - как новый объект возвращается в вызывающую процедуру:
// p - возвращается указатель на созданный объект
// о - возвращается сам созданный объект
// ro- возвращается ссылка на созданный объект
 
CBase *getpp(char* str) {
    CBase *p=new CBase(str);
    return p;
}
CBase getop(char* str) {
    CBase *p=new CBase(str);
    return *p;
}
CBase getoo(char* str) {
    CBase o(str);
    return o;
}
 
CBase& getrp(char* str) {
    CBase *p=new CBase(str);
    return *p;
}
 
CBase& getro(char* str) {
    CBase o(str);
    return o;
}
 
CBase* getpo(char* str) {
    CBase o(str);
    return &o;
}
 
void fun()
{
    CBase *pp=getpp("PP str1");     //+ новый объект создан в куче, на него вернули указатель. Потом надо не забыть удалить объект delete pp;
    CBase *po=getpo("PO str2");     //- локальный объект тут же удаляется, указатель на удаленный объек
    CBase op=getop("OP str3");      //- новый объект создан в куче, потом он копируется. НО старый объет остался в куче, т.е произошла утечка памяти
    CBase oo=getoo("OO str4");      //+ новый объект копируется, локальный удаляется. ВСЕ ОК. 
    CBase oro=getro("ORO str5");    //- локальный объект сначала удаляется, потом копируется! тяжелый случай
    CBase orp=getrp("ORP str6");    //- новый объект создан в куче, потом он копируется. НО старый объет остался в куче, т.е произошлаа утечка памяти
    CBase &rro=getro("RRO str7");   //- локальный объект тут же удаляется, ссылка на удаленный объект
    CBase &rrp=getrp("RRP str8");   //+ новый объект создан в куче, на него вернули ссылку. Но его надо не забыть удалить delete &rrp
    CBase o=oo; // копируется
    o=orp; // копируется
    
    // удаление объектов
    delete pp;
    delete &rrp; // ручное
    cout<<"Конец функции"<<endl;
}

А вот результат выполнения fun(), на основании которого сделаны комментарии:
Кликните здесь для просмотра всего текста

Base 1 PP str1
Base 1 PO str2
~Base 1 PO str2
Base 1 OP str3
CopyBase 2 @OP str3
Base 1 OO str4
CopyBase 2 @OO str4
~Base 1 OO str4
Base 1 ORO str5
~Base 1 ORO str5
CopyBase 2 @H€A
Base 1 ORP str6
CopyBase 2 @ORP str6
Base 1 RRO str7
~Base 1 RRO str7
Base 1 RRP str8
CopyBase 2 @@OO str4
Base = 3 @@OO str4
~Base 1 PP str1
~Base 1 RRP str8
Конец функции
~Base 3 @@OO str4
~Base 2 @ORP str6
~Base 2 @H€A
~Base 2 @OO str4
~Base 2 @OP str3

Выводы:
Из 8 рассмотренных вариантов осталось 3.
1-й и 8-й варианты создают объект в куче, нет копирования (не тратится время). НО их надо удалять.
4-й тоже неплох (если знаешь, как происходит копирование и объект небольшой).

В общем 1-й и 4-й варианты работают так, словно объект создан через конструктор.
А 8-й удобен в плане использования: обращение к полям через точку '.' (как в 4), но необходимость удаления объекта неочевидна.
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
04.08.2013, 11:59
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Особенности использования указателей и ссылок в C++ при возврате из функции (C++):

Ступор в возврате указателей и ссылок - C++
Изучаю С++ базовый курс от Шилтда, все проходил на твердую четверку, и на предпоследней теме про системы ввода-вывода абсолютно запоролся...

Объяснить нюансы использования указателей, массивов и ссылок в функциях - C++
Здравствуйте. Скажите, а что выводит arry значения элемента массива или его адрес? А как понимать что функция возвращает ссылку на int?...

Особенности использования функции rand() в С++ - C++
Не пойму почему Visual Studio ругается когда я создал класс и описывая его хочу использовать функцию rand() подключая при этом и stdlib.h и...

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

Затираются значения при возврате из функции initializer_list - C++
Дарова. Объясните, почему при возврате объекта инитиализер_лист из функции значения этого листа трутся #include &lt;iostream&gt; using...

Как избежать копирования вектора, при возврате его из функции? - C++
Вопрос простой. В классе есть поле вектор, член - данные. Методом я возвращаю его const std::vector&lt; unsigned char &gt;&amp;...

16
Jupiter
Каратель
Эксперт С++
6559 / 3980 / 227
Регистрация: 26.03.2010
Сообщений: 9,273
Записей в блоге: 1
Завершенные тесты: 2
04.08.2013, 12:01 #2
и??
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 12:30  [ТС] #3
И все Тема для чтения и для того, чтобы давать на нее ссылки (а не объяснять много раз одно и то же).
0
0x10
2475 / 1648 / 247
Регистрация: 24.11.2012
Сообщений: 4,070
04.08.2013, 12:32 #4
А для того, чтобы не греть голову на тему "кто это должен удалять?", "как быть с копированием больших объектов?" были придуманы такие вещи как rvalue-ссылки, move-конструкторы и операторы присваивания, а также умные указатели.
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 12:38  [ТС] #5
Я в курсе. А еще придумали кучу языков, где нет указателей. Кто не хочет греть - пусть проходит мимо
0
0x10
2475 / 1648 / 247
Регистрация: 24.11.2012
Сообщений: 4,070
04.08.2013, 12:42 #6
Т.е. автор описывает возможные варианты, рассказывает каких проблем мы огребем во всех случаях, но игнорирует прямые решения этих проблем. Ок, молчу.
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 12:52  [ТС] #7
1. Я в курсе других вариантов. А еще придумали кучу языков, где нет указателей.
2. Не надо молчать - никто не запрещает создать тему и рассмотреть в ней примеры с умными указателями rvalue-ссылками и т.п.
3. Если в проекте используются сотни фукций без "умных указателей" - будешь переписывать их все?
0
0x10
2475 / 1648 / 247
Регистрация: 24.11.2012
Сообщений: 4,070
04.08.2013, 12:55 #8
Цитата Сообщение от zer0mail Посмотреть сообщение
3. Если в проекте используются сотни фукций без "умных указателей" - будешь переписывать их все?
Из смартпоинтера всегда можно получить обычный указатель. Если же в боевом проекте на С++ изначально везде используется ручное управление памятью, это уже серьезный повод хорошо задуматься на тему адекватности такого решения.
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 13:06  [ТС] #9
Я по образованию физик. А физики используют компьютер как инструмент расчетов, а не как средство демонстрации своей программистской крутизны. И полно библиотек написано на простом Си или С++ с простыми указателями, переписывать которые никто не собирается. А есть даже написанные на Фортране (и немало).

Если найдутся желающие заменить систему управления ядерным реактором на новую, с "современными смарт-указателями", то скорее заменят этих желающих, а не систему.
1
0x10
2475 / 1648 / 247
Регистрация: 24.11.2012
Сообщений: 4,070
04.08.2013, 13:07 #10
Цитата Сообщение от zer0mail Посмотреть сообщение
А физики используют компьютер как инструмент расчетов, а не как средство демонстрации своей программистской крутизны.
Не в крутизне дело, а в упрощении кода.
Цитата Сообщение от zer0mail Посмотреть сообщение
И полно библиотек написано на простом Си или С++ с простыми указателями, переписывать которые никто не собирается.
И это нормально. Но использование в проекте сторонней библиотеки не означает, что нужно полностью перенимать ее стиль.
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 13:09  [ТС] #11
Не так давно в интернете были вакансии программистов для АЭС в Канаде. Так программы и компьютеры там 30-летней давности и планируют их эксплуатировать до 2050 года.
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
6503 / 3139 / 307
Регистрация: 04.12.2011
Сообщений: 8,661
Записей в блоге: 5
04.08.2013, 13:23 #12
zer0mail, дружище вот тут в 8-м варианте:
C++
1
2
3
4
CBase* getpo(char* str) {
    CBase o(str);
    return &o;
}
возврат ссылки на локальную переменную.
Вообще, указатель, ссылка и переменная-значение дают массу вариантов передачи и возврата. Передача по ссылке, кроме всего ещё и не всегда передача типа-ссылки. Если добавить к этому варианты с вновь создаваемыми объектами, то это будет книга. А дальше, - указатели на полиморфные типы... Не подъёмно.
Даже если не описывать задачу для каждого случая. То есть не пытаться обосновать, а когда вот так сделать лучше чем эдак. Не лучше ли рассматривать каждый случай отдельно?
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 13:45  [ТС] #13
Спасибо, IGPIGP, не забываешь . В 8-м тесте вызывается getrp и объект создается в куче, так что ссылка на него корректна.
А getpo вызывается во 2-м тесте и я пишу, что она не подходит (поскольку объект сразу удаляется).

Проблема может быть с 5-м тестом, кода сначала удаление а потом компирование (программа может просто "вылететь").
0
IGPIGP
Комп_Оратор)
Эксперт по математике/физике
6503 / 3139 / 307
Регистрация: 04.12.2011
Сообщений: 8,661
Записей в блоге: 5
04.08.2013, 13:49 #14
Цитата Сообщение от zer0mail Посмотреть сообщение
А в 8-м тесте вызывается getrp и объект создаетсся в куче, так что ссылка на него корректна.
Верно, я запутался в подсчете, да и записано у Вас в комментарии всё. Извините.
0
zer0mail
2354 / 1984 / 198
Регистрация: 03.07.2012
Сообщений: 7,117
Записей в блоге: 1
04.08.2013, 13:52  [ТС] #15
Да не за что - я рад, что ты читаешь мои опусы хотя бы тут (в физику я давно не пишу).
0
04.08.2013, 13:52
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
04.08.2013, 13:52
Привет! Вот еще темы с ответами:

Применение std::move к локальной переменной при возврате из функции - C++
Response HostHandler::notFound() const { Response resp; resp.status = 404; return std::move(resp); } Имеет ли это...

Почему не вызывается конструктор копирования при возврате объекта из функции - C++
Добрый день! Насколько мне известно, конструктор копирования вызывается: 1) при передачи объекта как аргумента функции ...

При возврате объекта из функции пишет, что для объекта не определен констуктор копирования - C++
Пишу шаблонный класс матриц. Делаю, значит, переопределение оператора + для двух матриц одного типа. Но компилятор ругается на эту функцию ...

Особенности использования const_cast - C++
Почему не работает код: 1. const int* const pInt = new int(0); const_cast&lt;int*&gt;(pInt) = new int(4); 2. void...


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

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

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