Професионална програма
Loading...
Vik1099 avatar Vik1099 2 Точки

Кога трябва да връщаме функция като референция

Здравейте,

Започнах да се обърквам със връщането на функции като референции. Не мога да си обясня точно кога трябва и кога не трябва. Знам, че при operator overloading използваме референции, когато искаме да направим chaining (но това не мога да си го обясня как точно работи), също знам, че i/ostream op. overloading могат да се използват само с референции. Но има други случаи, в които кода ми връща същия резултат без значение дали някои от функциите са били подадени като референции или не.

 

#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <sstream>

class Company {
public:
	Company(int id, const std::string& name, const std::vector<std::pair<char, char>>& employees);

	std::vector<std::pair<char, char>> getEmployees() const;
	//const std::vector<std::pair<char, char>>& getEmployees() const;

	std::string toString() const;

	std::string operator+(const std::string& s) const;

	Company& operator+=(const std::pair<char, char>& employee);
	//Company operator+=(const std::pair<char, char>& employee);

private:
	int _id;
	std::string _name;
	std::vector<std::pair<char, char>> _employees;
};

Company::Company(int id, const std::string& name, const std::vector<std::pair<char, char>>& employees)
	: _id{ id }, _name{ name }, _employees{ employees } {}

std::vector<std::pair<char, char>> Company::getEmployees() const {
	return _employees;
}

std::string Company::toString() const {
	std::ostringstream stream;
	stream << _id << " " << _name << " ";
	for (size_t i = 0; i < _employees.size(); ++i) {
		auto initials = _employees[i];
		stream << initials.first << initials.second;
		if (i < _employees.size() - 1) {
			stream << " ";
		}
	}
	return stream.str();
}

std::string Company::operator+(const std::string& s) const {
	return toString() + s;
}

Company& Company::operator+=(const std::pair<char, char>& employee) {
	_employees.push_back(employee);
	return *this;
}

int main() {
	Company c(42, "Something Inc.", { {'G', 'L'}, {'A', 'B'}, {'N', 'M'} });
	c += {'W', 'P'};

	std::cout << c + "<- this is a cool company" << std::endl;
	return 0;
}

 

Ето в горния код примерно мога да върна std::vector getEmployees() и operator+=() функциите със референция или без референция и кода сякаш работи по същия начин, но не знам как е по правилино.

Поздрави, 

Виктор

Тагове:
0
C++ OOP 16/11/2021 12:59:58
MartinBG avatar MartinBG 4356 Точки

Зависи какво ще се прави с върната референция:

- искаме само да ползваме данните - например за отпечатване или валидация/търсене. В този случай е добре да се застраховаме и да връщаме const& за да предотвратим нежелани модификации.

- искаме да модифицираме директно данните  - това най-често е лоша идея и трябва да се избягва.

При връщане на референция спестяваме създаването на копие на данните. За прости типове няма смисъл от това, но при по-сложни обекти или колекции разликите може да са значителни, но дори и тогава трябва да се внимава, защото има значение как ще запазим резултата от извикването на метода:

- пазим го като обект - имплицитно ще се създаде копие на референтните данни, т.е. губим ползата от връщането на референция. Има случаи, когато е необходимо точно това.

- пазим го като референция - трябва да се внимава реферираните данни/обект да не излязат от скоуп или да бъдат унищожени, защото това ще инвалидира и референцията. 

 

Да се върнeм към примерите:

Company& operator+=(const std::pair<char, char>& employee);

Връщане по референция има смисъл, ако възнамеряваме да чейнваме наколко извиквания на метода, напр:

	Company c(42, "Something Inc.", { {'G', 'L'}, {'A', 'B'}, {'N', 'M'} });

	c.operator+=({'W', 'P'}).operator+=({'F', 'F'}); // <-here

Макар и работещ, кодът не е особено приятен за четене и е по-скоро безсмислено да връщаме референция за += оператора.

 

const std::vector<std::pair<char, char>>& getEmployees() const;

GetEmployees() връща const& към private мембъра _employees. т.е. данните са защитени и не им правим копие. Недостатък е, че expose-ваме вътрешен имплементационен детайл за нашия клас и ако в даден момент решим да сменим вектора със set, array, или обикновен масив..., това може а предизвика промени и в кода на всички ползватели на класа. 

Ето един вариант за заобикаляне на този проблем - вместо директен достъп до вътрешния контейнер, връщаме итератори към съдържанието му:

class Company {
public:
    //....
    const auto begin() const {
        return _employees.cbegin();
    }

    const auto end() const {
        return _employees.cend();
    }
    //....
}



int main() {
	Company c(42, "Something Inc.", { {'G', 'L'}, {'A', 'B'}, {'N', 'M'} });
	c.operator+=({'W', 'P'}).operator+=({'F', 'F'});

    for (const auto& empl : c) { // <- use iterators here
        std::cout << empl.first << ", " << empl.second << std::endl;
    }

	std::cout << c + "<- this is a cool company" << std::endl;
	return 0;
}

/*
Output

G, L
A, B
N, M
W, P
F, F
42 Something Inc. GL AB NM WP FF<- this is a cool company

*/

 

0
18/11/2021 09:24:24
Vik1099 avatar Vik1099 2 Точки

Благодаря за отговора.

1
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.