Menu
Coddy logo textTech

Спецификаторы доступа: подробный разбор

Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy. Урок 35 из 104.

Теперь, когда ты понимаешь три спецификатора access, давай разберёмся, как они ведут себя, когда у тебя есть несколько объектов одного и того же class. Распространённое заблуждение состоит в том, что private означает «только этот объект», но на самом деле это означает «только этот class».

Объекты одного и того же класса могут обращаться к закрытым членам друг друга:

class Person {
    std::string secret;
    
public:
    Person(std::string s) : secret(s) {}
    
    void readSecret(const Person& other) {
        // Доступ к приватному члену другого объекта — разрешён!
        std::cout << other.secret;
    }
};

Это работает, потому что access control обеспечивается на уровне class, а не на уровне объекта. Метод readSecret является частью Person class, поэтому он может получать доступ к закрытым членам любого объекта Person. Такая конструкция позволяет таким операциям, как конструкторы копирования и операторы сравнения, работать с внутренним состоянием других объектов.

Вы также можете несколько раз переключаться между спецификаторами access внутри class:

class Mixed {
public:
    void publicMethod1();
    
private:
    int privateData;
    
public:
    void publicMethod2();    // Снова public
    
private:
    void privateHelper();    // Снова private
};

Хотя это допустимый C++, обычно для удобства чтения лучше группировать члены по уровню access. В большинстве руководств по стилю рекомендуется сначала перечислять public-члены (поскольку они образуют интерфейс class), затем protected, а после них — private.

challenge icon

Задание

Легко

Давайте создадим систему сравнения кошельков, демонстрирующую, как объекты одного и того же class могут получать доступ к приватным членам друг друга. Это важное понимание принципа управления доступом в C++: он применяется на уровне class, а не на уровне объекта.

Вы создадите два файла, чтобы организовать код:

  • Wallet.h: определите class Wallet, который хранит приватные финансовые данные, но позволяет сравнивать кошельки друг с другом. Ваш class должен иметь:
    • Приватные члены: ownerName (строка) и balance (число double)
    • Конструктор, принимающий имя владельца и initial balance
    • Публичный getter getOwnerName(), возвращающий имя владельца
    • Метод hasMoreThan(const Wallet& other), который сравнивает balance этого кошелька с приватным balance другого кошелька и возвращает true, если в этом кошельке больше денег
    • Метод combinedBalance(const Wallet& other), возвращающий сумму приватных balance обоих кошельков
    • Метод transferTo(Wallet& other, double amount), который перемещает деньги из этого кошелька в another кошелёк. Он должен напрямую изменять приватные balance обоих кошельков (только если amount положителен и не превышает balance этого кошелька)
  • main.cpp: продемонстрируйте доступ к приватным членам между объектами. Считайте из input два имени владельцев и два значения balance (четыре строки: name1, balance1, name2, balance2), затем:
    • Создайте два объекта Wallet с указанными во входных данных значениями
    • Выведите "Combined wealth: $<amount>" с использованием combinedBalance()
    • Сравните кошельки и выведите "<name> has more money" для владельца с более высоким balance (используйте hasMoreThan(), чтобы определить это)
    • Переведите 100.0 из первого кошелька во второй с помощью transferTo()
    • Выведите "After transfer:"
    • Снова выведите "Combined wealth: $<amount>" (значение должно остаться неизменным)
    • Снова сравните кошельки и выведите "<name> has more money" (результат может измениться)

Ключевая идея заключается в том, что такие методы, как hasMoreThan() и transferTo(), могут напрямую обращаться к приватному члену balance другого объекта Wallet: геттеры не нужны. Это работает, потому что управление доступом в C++ основано на class: любой код внутри class Wallet может обращаться к приватным членам любого объекта Wallet.

Форматируйте все денежные значения с двумя знаками после запятой, используя std::fixed и std::setprecision(2) из <iomanip>. Преобразуйте входные значения balance с помощью std::stod().

Попробуйте сами

#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"

using namespace std;

int main() {
    // Прочитать ввод: name1, balance1, name2, balance2
    string name1, name2;
    string balanceStr1, balanceStr2;
    
    getline(cin, name1);
    getline(cin, balanceStr1);
    getline(cin, name2);
    getline(cin, balanceStr2);
    
    double balance1 = stod(balanceStr1);
    double balance2 = stod(balanceStr2);
    
    // Установить форматирование вывода для денежных значений
    cout << fixed << setprecision(2);
    
    // TODO: Создать два объекта Wallet с входными значениями
    
    // TODO: Print combined wealth using combinedBalance()
    // Format: "Combined wealth: $<amount>"
    
    // TODO: Compare wallets using hasMoreThan() and print who has more money
    // Format: "<name> has more money"
    
    // TODO: Перевести 100.0 из первого кошелька во второй с помощью transferTo()
    
    // TODO: Print "After transfer:"
    
    // TODO: Print combined wealth again
    
    // TODO: Compare again and print who has more money
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор C++