Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.84/58: Рейтинг темы: голосов - 58, средняя оценка - 4.84
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393

Константные функции-члены класса, возвращающие ссылку на константу

08.08.2011, 21:09. Показов 13172. Ответов 64
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Упражняюсь по книге Липпмана, выполняю задания по теме классы.
Необходимо реализовать класс person, который способен хранить имя и адрес человека, а также создать функции, возвращающие при обращении имя и адрес.
Вопрос, который ставит автор книги: "Должны ли эти функции быть константными? Объясните почему."
Вот тут я начал путаться. Вот моя реализация:
файл person.h
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
#ifndef PERSON_H_
#define PERSON_H_
 
class person {
public:
    //конструктор, принимающий две строки типа string
    person( std::string &_name, std::string &_addres ) :
        name( _name ), addres( _addres ) {
    }
    //метод, возвращающий addres
    const std::string &get_addres() const {
        return addres;
    }
    //метод, возвращающий name
    const std::string &get_name() const {
        return name;
    }
 
private:
    //поле для хранения имени
    std::string name;
    //поле для хранения адреса
    std::string addres;
};
 
#endif /* PERSON_H_ */
Для тестирования класса, файл test_class_person.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include "person.h"
using namespace std;
 
int main() {
    string str1 = "Alexander";
    string str2 = "Zelenogradsk";
    person pers( str1, str2 );
    cout << pers.get_name() << " " << pers.get_addres() << endl;
    
    string str3 = pers.get_name();
    str3 += "222";
    cout << str3 << " " << pers.get_name();
    return 0;
}
Я решил так
1) Константными эти функции должны быть, так как они не меняют значений переменных-членов объекта.
2) Возвращать они должны лишь константную ссылку на строку, а не просто тип string, дабы избежать ненужного копирования.
Прав ли я? Если нет, то объясните почему.
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
08.08.2011, 21:09
Ответы с готовыми решениями:

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

Константные функции-члены ?
Для чего использовать #define ? не имеют права изменять поля класса почему ? class Array { ... inline double operator...

класс «Строка» (данные-члены класса – строчка, функции-члены класса – операции)
Помогите пожалйуста с реализацией программы... Реализовать класс «Строка» (данные-члены класса – строчка (указатель на массив, ...

64
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393
09.08.2011, 18:51  [ТС]
Студворк — интернет-сервис помощи студентам
2 Deviaphan - Спасибо за демонстрацию, повторное использование действительно невозможно и сделать &sss ссылкой на не константную переменную тоже невозможно.

Если изменить методы класса person так:

C++
1
2
3
4
5
6
7
8
9
10
...
//метод, возвращающий addres
    std::string get_addres() const {
        return addres;
    }
    //метод, возвращающий name
    std::string get_name() const {
        return name;
    }
...
То такие варианты использования уже невозможны:
C++
1
const string &sss = ( person( str1, str2 ).get_name() );
C++
1
string &sss = ( person( str1, str2 ).get_name() );
А вот такие варианты уже возможны:
C++
1
const string sss = ( person( str1, str2 ).get_name() );
C++
1
2
string sss = ( person( str1, str2 ).get_name() );
sss += str2;
Насколько я понял, вариант, когда методы класса возвращают не ссылку, а объект - предпочтителен, так как более защищен от возникновения ошибок. Deviaphan, я правильно понял твою демонстрацию?
0
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
09.08.2011, 18:59
Цитата Сообщение от Roof Посмотреть сообщение
я правильно понял твою демонстрацию?
Правильно.
Но я предпочитаю возвращать константную ссылку для сложных объектов (в том числе и строк), но не использую потенциально опасные конструкции (как в примере). Объекты, локальные для строки кода нужно использовать очень аккуратно и лучше вообще их не использовать, пока не поймёшь до конца принцип их работы и время жизни этих объектов.

Т.е. лучше возвращать константную ссылку, но использовать код вида:
C++
1
2
Person temp( str1, str2 );
const string &sss = temp.get_name();
1
ниначмуроФ
 Аватар для PointsEqual
851 / 535 / 110
Регистрация: 12.10.2009
Сообщений: 1,913
09.08.2011, 19:26
Цитата Сообщение от Roof Посмотреть сообщение
Насколько я понял, вариант, когда методы класса возвращают не ссылку, а объект - предпочтителен

Не по теме:

наконет-то свершилось

0
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393
09.08.2011, 22:01  [ТС]
Цитата Сообщение от Deviaphan Посмотреть сообщение
Но я предпочитаю возвращать константную ссылку для сложных объектов (в том числе и строк)
Вот и я думаю, что константную ссылку на объект возвращать продуктивней, дабы избежать копирования. Но опять же, PointsEqual рекомендует возвращать сам объект, дабы избежать возможности возникновения указанных выше ошибок, которые могут возникнуть при использовании моего класса.

Однозначного ответа, как я понимаю тут нет.
Из всего сказанного я сделал такие выводы:
- можно возвращать константную ссылку на объект, надеясь на то, что пользователь моего класса не будет писать подобные инструкции:
C++
1
const string & sss = ( person( str1, str2 ).get_name() );
- можно возвращать сам объект, пренебрегая копированием, но не боясь приведенных выше ошибок.
0
Модератор
Эксперт PythonЭксперт JavaЭксперт CЭксперт С++
 Аватар для easybudda
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,980
09.08.2011, 23:11
Roof, однозначно PointsEqual правильно говорит. Ссылка действительно может пережить свой объект. Так, что, возвращайте строку, а не ссылку.
2
бжни
 Аватар для alex_x_x
2473 / 1684 / 135
Регистрация: 14.05.2009
Сообщений: 7,162
09.08.2011, 23:29
кроме того к вопросу о копировании - в продвинутых библиотеках (в Qt точно знаю, полагаю, что так сделано и в многих реализациях stl) контейнеры реализуют идиому "письмо - конверт" (манипулятор/тело), обеспечивающую эффективные операции копирования - фактически применение принципа копирования при записи
это я к тому, что зная, что операции копирования контейнеров растратные, нужно учитывать, что они могут быть много хитрее и на самом деле не приводить к выделению памяти и копированию данных
и не пренебрегать безопасностью
2
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393
09.08.2011, 23:59  [ТС]
easybudda, alex_x_x - спасибо за ответы, очень помогли.
0
Модератор
Эксперт PythonЭксперт JavaЭксперт CЭксперт С++
 Аватар для easybudda
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,980
10.08.2011, 00:30
На самом деле всё хитрее...
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
#include <iostream>
#include <string>
 
class person {
public:
        //конструктор, принимающий две строки типа string
        person( const std::string &_name, const std::string &_address ) :
                name( _name ), address( _address ) {
        }
        //метод, возвращающий addres
        const std::string &get_address() const {
                return address;
        }
        //метод, возвращающий name
        const std::string &get_name() const {
                return name;
        }
        void set_name(const std::string & _name) { 
            name = _name; 
        }
        void set_address(const std::string & _address) { 
            address = _address; 
        }
 
private:
        //поле для хранения имени
        std::string name;
        //поле для хранения адреса
        std::string address;
};
 
int main(){
    person p("Vasya", "Hell");
    
    const char * n = p.get_name().c_str();
    std::cout << n << std::endl;
    p.set_name("Petya");
    std::cout << n << std::endl;
    
    return 0;
}
Два раза выведет Васю. Думаю, функции get_name, get_address (кстати, это слово с двумя s пишется) на самом деле создают константные копии строк, ссылки на которые и возвращают. Сэкономить в любом случае не получится, морока одна. Возвращайте строки.
2
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393
10.08.2011, 06:27  [ТС]
2 easybudda,
Цитата Сообщение от easybudda Посмотреть сообщение
Думаю, функции get_name, get_address (кстати, это слово с двумя s пишется) на самом деле создают константные копии строк, ссылки на которые и возвращают. Сэкономить в любом случае не получится, морока одна. Возвращайте строки.
Об этом не догадывался, большое спасибо за информацию.
Однозначно - буду возвращать строку, а не ссылку.

Но мне не понятен смысл Вашего примера. Вот как я его понял:
Методы get_name() и get_address() возвращают ссылку на константу, как в Вашем примере.
C++
1
2
3
4
5
6
7
8
...
person p("Vasya", "Hell");
 
const char * n = p.get_name().c_str();  //строка n в стиле C инициализирована строкой "Vasya"
std::cout << n << std::endl; //вывели n
p.set_name("Petya");    //никакого отношения к n не имеет
std::cout << n << std::endl; //снова вывели n
...
В Вашем примере выводится два раза Vasya. Как я понимаю - это логично, ведь n инициализирована вначале, потом хоть сколько меняй состояние объекта p - на n это никак не отразится.

Также два раза выведется Vasya, если методы get_name() и get_address() будут возвращать константные строки или просто строки. Соответственно объявление и инициализацию переменной n я проводил так:
C++
1
const std::string n = p.get_name();
или
C++
1
std::string n = p.get_name();
А возвращать string или const string, насколько я понял нет никакой разницы.

Т.е. осталось понять, что именно Вы хотели сказать в Вашем примере.
0
Модератор
Эксперт PythonЭксперт JavaЭксперт CЭксперт С++
 Аватар для easybudda
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,980
10.08.2011, 09:54
Цитата Сообщение от Roof Посмотреть сообщение
А возвращать string или const string, насколько я понял нет никакой разницы.
В С функции не должны возвращать константные значения. На счёт С++ не знаю, но в принципе как-то не за чем... К примеру, если есть фукнция, переводящая строку в верхний регистр, то вот такая конструкция с константной строкой скорее всего не будет работать
C++
1
std::cout << stringToUppercase(some_person.get_name()) << std::endl;
1
Эксперт С++
 Аватар для CyBOSSeR
2348 / 1721 / 149
Регистрация: 06.03.2009
Сообщений: 3,675
10.08.2011, 10:30
Цитата Сообщение от Deviaphan Посмотреть сообщение
Но я предпочитаю возвращать константную ссылку для сложных объектов
В таких случаях умные указатели предпочтительнее, возвращать ссылки - слишком потенциально опасное действие, чтобы его производить.
1
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
10.08.2011, 10:40
умные указатели предпочтительнее
Абсолютно да!
Но чтобы возвращать умные указатели на внутренние данные объекта, эти данные должны быть умными указателями. Что не всегда желательно и возможно.
С другой стороны, С++ вообще очень потенциально опасный язык и речь должна идти о культуре программирования и опыте программиста. Когда пишешь простой и понятный код, опасные конструкции встречаются гораздо реже.
0
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393
10.08.2011, 15:22  [ТС]
Спасибо всем ответившим. На свой вопрос получил однозначный ответ.
0
Модератор
Эксперт PythonЭксперт JavaЭксперт CЭксперт С++
 Аватар для easybudda
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,980
10.08.2011, 15:42
Roof, ну и в заключении поясню суть своего примера: я его дома на ноуте с debian делал, там, напомню, два раза Vasya вывелось. А вот, как этот же самый код (отсюда скопировал) с другими компиляторами работает:


Можно долго гадать, или разбираться, почему оно так, но лучше просто не оставлять такие вещи на откуп компилятору...
2
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
10.08.2011, 15:50
Цитата Сообщение от easybudda Посмотреть сообщение
почему оно так
Второй раз не Вася вывелся, а Вас(стрелочка).
0
53 / 53 / 8
Регистрация: 21.03.2009
Сообщений: 371
10.08.2011, 16:03
Возврат const string & необходим чтобы избежать возможности изменять private часть класса без использования интерфейса
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
#include<iostream>
#include<string>
using namespace std;
class A {
private:
string str;
public:
    A();
    string & fun();
    void show(){
     cout<<str;
    }
};
int main() {
  A obj;
  obj.show();
  obj.fun()="kol";//изменили str
  cout<<endl;
  obj.show();
  cin.get();
  return 0;
}
A::A(){
str="gflj";
}
string & A::fun(){
return str;
}
0
Модератор
Эксперт PythonЭксперт JavaЭксперт CЭксперт С++
 Аватар для easybudda
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,980
10.08.2011, 16:04
Цитата Сообщение от Deviaphan Посмотреть сообщение
Второй раз не Вася вывелся, а Вас(стрелочка).
Да, я заметил. В cygwin указатель указывает на буфер строки, которая к моменту второго обращения уже не существует. Мелкомягкий каким-то чудом сохраняет указатель, хоть она к тому времени и поменялась (на сколько знаю, оператор = должен бы уничтожать старую строку и создавать новую), а вот в BSD снова два Васи и без стрелок:
1
Делаю внезапно и красиво
Эксперт С++
 Аватар для Deviaphan
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
10.08.2011, 16:08
Цитата Сообщение от easybudda Посмотреть сообщение
Мелкомягкий каким-то чудом сохраняет указатель
Не исключено, что была создана копия строки, в целях оптимизации. Насколько я помню, короткие строки хранятся в массиве в самом объекте, без выделения памяти. А раз обещался const компилятор мог создать копию. Поэтому изменив оригинальный объект осталась строка в копии... А вообще, чего гадать на ошибках, лучше так не писать и усё.)
1
Модератор
Эксперт PythonЭксперт JavaЭксперт CЭксперт С++
 Аватар для easybudda
12843 / 7592 / 1766
Регистрация: 25.07.2009
Сообщений: 13,980
10.08.2011, 16:25
Цитата Сообщение от Deviaphan Посмотреть сообщение
Поэтому изменив оригинальный объект осталась строка в копии...
Так в том и прикол, что мелкомягкий не создавал копии, просто вернул указатель на массив char* от строки-члена класса, который сохранился после присвоения строке другого значения, скорее всего по причине
Цитата Сообщение от Deviaphan Посмотреть сообщение
короткие строки хранятся в массиве в самом объекте, без выделения памяти
А вот про это
Цитата Сообщение от Deviaphan Посмотреть сообщение
лучше так не писать
я собственно и говорил.

Цитата Сообщение от lavan Посмотреть сообщение
Возврат const string & необходим чтобы избежать возможности изменять private часть класса без использования интерфейса
Спасибо, Кэп, разобрались уже...
0
 Аватар для Roof
155 / 155 / 44
Регистрация: 03.11.2010
Сообщений: 393
10.08.2011, 17:56  [ТС]
2 easybudda - я все примеры тестирую на Ubuntu с g++ и Ваш пример у меня вывел два раза Vasya, как и у Вас на Debain и BSD.

Цитата Сообщение от easybudda Посмотреть сообщение
Можно долго гадать, или разбираться, почему оно так, но лучше просто не оставлять такие вещи на откуп компилятору...
- с этим полностью согласен.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
10.08.2011, 17:56

Почему нехорошо себя ведёт конструктор класса (не компилится ничё), если ему параметром ссылку на константу?
То есть вообще непонятно ничё, вот, смотрите, этот пример не компилится: #include &lt;stdio.h&gt; class fee { public: //Не...

ООП - Указатели на функции члены класса
Не пойму в чем ошибка, MVS2017 при компиляции выдает целую стопку ошибок: #include &lt;iostream&gt; using namespace std ...

Массив указателей на функции-члены класса
Задача заключается в том, что в private надо создать статическую переменную символьного типа, в protected просто переменные вещественного...

Константные массивы как члены класса и ошибка Call of nonfunction
Здравствуйте. Я по двум связанным вопросам. 1) Требуется объявить константный массив как член класса. По статейки с форума rsdn (не...

Использование указателей на функции-члены внутри самого класса
День добрый форум! Возник такой вопрос. В классе А есть 3 функции и массив, в котором хранятся адреса этих функций. class A ...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Нейтральные знания, чистый код - бла-бла-бла-бла, на самом деле кликбейт и самореклама, плагиат, и вот почему
Hrethgir 27.07.2026
То-есть отклонение такой публикации говорит само за себя, и пусть только возьмут на вооружение после отклонения публикации - это будет чистейшим актом плагиата. Отклонял Хабр. Дословно, отклонённая. . .
тв 16 бой ии
anaschu 27.07.2026
Великий Перелом ИИ: Как уравнения ОДУ Radau дожали цензурные фильтры Алисы Фиксируем в мемофонде Теории Всего беспрецедентный факт в истории ИИ-зондирования. В затяжном многораундовом. . .
мв 15. непроверенное, возможно, глюк
anaschu 27.07.2026
НАУЧНО-АНАЛИТИЧЕСКИЙ ОТЧЕТ. РАЗДЕЛ 1. 1: «НАУКА» (РАСШИРЕННАЯ СТЕХИОМЕТРИЧЕСКАЯ И ГЕНЕТИЧЕСКАЯ ВЕРСИЯ)Тема: Теоретическое обоснование инвариантности 19-мерного тензорного ядра непрерывных ОДУ и. . .
Очистка реквизитов и табличных частей документа при копировании (вариант 2)
Maks 26.07.2026
Алгоритм из решения ниже разработан на примере нетипового документа "ЗаявкаНаРаботу", разработанного в КА2. Задача: Заменить алгоритм запрета копирования документов для сотрудников с ролью "Стажер",. . .
Доктрина интенционального знания - Доктрина для портала "Срез".
Hrethgir 25.07.2026
Может найдётся кто захочет оценить доктрину. . . Написания правил участия для меня роскошь, требующая лимита времени, поэтому все сообщения не прошедшие модерацию будут видны только участникам портала,. . .
сукцессия 44. Решил подать на припринт в межународные сервисы препринтов. Но нужно одобрение от ученых
anaschu 25.07.2026
Английский вариант. Пока кто то не одобрит мою личность, мне не получиться это опубликовать на препринте. Но заявку на публикацию статьи я сегодня подам.
сукцессия 43. Вторая научная статья за месяц- прайминг и гатгил
anaschu 25.07.2026
две стороны одной монеты
Более приземисто - Эстафету хвоста в .cdl (деревья эстафеты в сад).
Hrethgir 24.07.2026
В будущем, после написания блока инверсии обхода дерева (эстафеты хвоста), я планирую вернуться к нашему прошлому разговору о том, обладают ли знания целеполаганием. Тогда я пришел к выводу, что. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru