Что касается передачи по значению для примитивов типа int (и им подобные) и возвращение по указателю тонкостей нет, в то время как передачу параметра по значению для сложных типов и передачу параметра по ссылке, следует хорошо понимать.
Пусть есть некоторый класс данных SmthData:
class SmthData
{
public:
// конструктор по-умолчанию
SmthData(): value(0)
{
std::cout << "SmthData::SmthData()" << std::endl;
}
// копирующий конструктор
SmthData(const SmthData &other):value(other.value)
{
std::cout << "SmthData::SmthData(const SmthData&)" << std::endl;
}
int value;
}
замечание: Для лучшего понимания будем отслеживать явные и не явные моменты создания SmthData (для чего и пишется в std::out при вызове конструкторов).И есть некоторый класс Foo, который должен возвращать SmthData:
class Foo
{
public:
void print()
{
std::cout << "Foo::print data.value:" << data.value << std::endl;
}
// здесь будет функция getSmthData
private:
SmthData data;
};
замечание: При создании экземпляра класса Foo будет создан экземпляр класса SmthData (будет вызван конструктор по-умолчанию) в месте декларации поля data класса Foo:
private:
SmthData data;
Проверяем:int main(int argc, char *argv[])
{
Foo foo;
return 0;
}
и запускаем:$ g++ -W -Wall main.cpp -o main && ./main
SmthData::SmthData()
I. Первый вариант функции Foo::getSmthData: возврат по значению
SmthData smthData()
{
return data;
}
Проверяем SmthData d = foo.smthData();
std::cout << "d.value:" << d.value << std::endl;
d.value = 5;
std::cout << "d.value:" << d.value << std::endl;
foo.print();
результат:SmthData::SmthData(const SmthData&)
d.value:0
d.value:5
Foo::print data.value:0
Что произошло: в точке выхода из метода
return data;была создана копия объекта посредством вызова копирующего конструктора - дальнейшие изменения d не приводят к изменению data экземпляра foo.
II. Второй вариант функции Foo::getSmthData: возврат ссылки
SmthData &smthData()
{
return data;
}
Проверяем SmthData d = foo.smthData();
std::cout << "d.value:" << d.value << std::endl;
d.value = 5;
std::cout << "d.value:" << d.value << std::endl;
foo.print();
результат:SmthData::SmthData(const SmthData&)
d.value:0
d.value:5
Foo::print data.value:0
Что произошло: метод вернул ссылку на свойство Foo::data, а после был вызван оператор присвоения объекта по значению
SmthData d =и была создана копия объекта посредством вызова копирующего конструктора - точно так же дальнейшие изменения d не приводят к изменению data экземпляра foo.
II'. Используем второй вариант функции Foo::getSmthData возврат ссылки, но изменим теперь код проверки:
SmthData &d = foo.smthData();
std::cout << "d.value:" << d.value << std::endl;
d.value = 5;
std::cout << "d.value:" << d.value << std::endl;
foo.print();
результат:d.value:0
d.value:5
Foo::print data.value:5
Что произошло: Мы получили прямую ссылку на свойство Foo::data и можем её модифицировать, при этом копия объекта не создавалась.
III. Третий вариант функции Foo::getSmthData: возврат константной ссылки
const SmthData &smthData()
{
return data;
}
Проверяем const SmthData &d = foo.smthData();
std::cout << "d.value:" << d.value << std::endl;
foo.print();
результат:d.value:0
Foo::print data.value:0
Что произошло: Мы получили прямую ссылку на свойство Foo::data и т.к ссылка является константой, невозможно модифицировать объект (компилятор не позволит скомпилировать код типа d.value = 5;), копия объекта также не создавалась.
дополненено замечание: конечно же, используя мощь и средства C/C++ никто не мешает изменить объект путём приведения константной ссылки к константному указателю, который привести к не константному и далее модифицировать объект - но это есть bad code style:
const SmthData *cp = &d; // получаем указатель (константный)
SmthData *p = const_cast<SmthData*>(cp); // снимаем "константность"
p->value = 5;
p.s. такое же поведение сохраняется и при вызове метода с передачей параметра по значению, и по ссылке.
Комментариев нет:
Отправить комментарий