Доступ к члену класса std: vector из std: vector в классе с другим std: vector - PullRequest
1 голос
/ 30 мая 2020

Меня озадачивает, как получить доступ к std :: vector в классе с другим std :: vector. Предыдущее обсуждение ( Почему нормально возвращать «вектор» из функции? ) касается аналогичной проблемы, но кажется, что я что-то неправильно понял.

Вот набросок моего кода:

class Employee
    m_id;
public:
    set_id(int id);
    id();

In Employee.cpp
void Employee::set_id(const int id)
{
    m_id = id;          // m_id is 5
}

int Employee::id() const
{
    return m_id;        // m_id is rubbish
}

class Company
public:
    std::vector<Employee> m_employees;

In Company.cpp
void Company::addEmployee(const addEmployee employee) // A vant to pass a copy of employee
{
    m_employees.push_back(employee);
}
std::vector<Employee> Company::employees()
{
    return m_employees;
}

in myClass.h
std::vector<Company> m_companies;

in myClass.cpp  

int empl_id = 5;   // test value
m_companyIndex = 0;   // for test
m_emplIndex = 0;   // 

Company company; 
Employee employee;
m_companies.push_back(company);
m_companies.at(m_companyIndex).addEmployee(employee);

            m_companies.at(m_companyIndex).employees().at(m_emplIndex).set_id(empl_id);
int idRet = m_companies.at(m_companyIndex).employees().at(m_emplIndex).id(); 

idRet contans an apparently random number, instead of 5. Using a debugger shos that 

Если я создаю экземпляр Employee в myClass, я могу установить и прочитать m_id; idem для Company, поэтому проблема, похоже, связана с std :: vector m_employees; в классе Company. Может ли это иметь какое-то отношение к «оптимизации возвращаемого значения»? Кстати я использую g cc 7.5.4.

1 Ответ

0 голосов
/ 30 мая 2020

Как указывает @dxiv, ваша функция Company :: employee создает и возвращает копию списка m_employees. Таким образом, ваш код

m_companies.at(m_companyIndex).employees().at(m_emplIndex).set_id(empl_id);

преобразуется в:

Get the element of m_companies at the index m_companyIndex,
then construct a copy of its m_employees list,
then get the element of that copy at index m_emplIndex,
then set the id of that element to empl_id,
and then destroy the copy.

Это, конечно же, не меняет содержимое списка m_employees вообще.

Вы можете сделать свой код работать более или менее правильно, заставляя Company :: employee возвращать неконстантную ссылку на список сотрудников, а не копию. Однако такая функция не может быть вызвана для объекта Company 'const', поэтому вам, вероятно, также понадобится его версия 'const', которая также вернет ссылку на const.

Однако, раскрытие закрытого класса детали реализации, такие как m_employees, даже через неконстантную ссылку, опасны, потому что, если внешний код использует эту ссылку для изменения вектора m_employees, тогда класс Employees не уведомляется, и любые другие внутренние данные, которые у него есть, которые могут зависеть содержимое вектора m_employees не может быть обновлено одновременно.

Поэтому, как правило, для внешнего кода лучше попросить содержащий класс (сотрудников) внести изменение, чем просить содержащий класс вернуть неконстантную ссылку, которую может использовать внешний код для внесения изменений. Таким образом, класс Employees может вносить изменения, а также обновлять любые другие внутренние данные, которые у него есть, которые могут зависеть от содержимого m_employees.

Например, вы можете добавить функцию Employees :: at, которая возвращает ссылку в элемент, чтобы указанная выше строка кода могла быть переписана как:

m_companies.at(m_companyIndex).at(m_emplIndex).set_id(empl_id);

или даже лучше:

m_companies.at(m_companyIndex).set_id(m_emplIndex, empl_id);

или даже лучше, если m_companies содержались в объекте Companies с именем 'companies':

companies.set_id(m_companyIndex, m_emplIndex, empl_id);

С помощью этих функций вы можете изменить Employees :: m_employees, чтобы использовать совершенно другую структуру данных (например, отсортированный список, связанный список или набор), не затрагивая ваш интерфейс вообще. Это уменьшает связь между внешним кодом и внутренними компонентами вашего класса и упрощает внесение изменений в будущем, если вам это нужно.

В общем, разумно учитывать Закон Деметры при разработке интерфейсов такого типа, особенно в отношении интерфейсов для изменения содержимого объектов: многие объекты плохо реагируют на изменение их внутреннего содержимого внешними классами без их ведома, поэтому лучше попросить содержащий объект изменить его подобъекты, когда это возможно, вместо того, чтобы разрешать неконстантные ссылки на них быть доступными извне класса.

...