[6장] 클래스 멤버와 멤버 함수: 객체지향 프로그래밍의 핵심

C++에서는 friend, protected, friend 함수, friend 클래스, friend 함수 템플릿 등 다양한 기능성 요소를 활용하여 클래스 간의 접근 권한을 관리하고 정보 공유를 조절할 수 있습니다. 이러한 기능들을 효과적으로 활용하여 객체 지향 프로그래밍에서의 유연한 설계와 정보 은닉을 구현할 수 있습니다

클릭하시면 확대된 이미지를 확인하실 수 있습니다.

초보자를 위한 C++ 200제:C++시작을위한최고의입문서! 설치부터문법배우고JSON응용까지레벨업!, 정보문화사

클래스 멤버

클래스 멤버란 클래스 내부에 선언된 변수나 상수를 의미합니다. 이러한 멤버들은 클래스의 객체(인스턴스)를 생성할 때마다 독립적으로 유지되며, 객체 간에 데이터를 공유할 수 있습니다.

예를 들어, 다음은 간단한 클래스 Person을 정의하고 클래스 멤버 nameage를 가지고 있는 예제입니다.

#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    int age;
};

[성안당]C++가 보이는 그림책, 성안당

멤버 함수

멤버 함수는 클래스 내에서 정의되며, 클래스의 객체를 조작하기 위해 사용됩니다. 멤버 함수는 클래스의 데이터 멤버에 접근할 수 있고, 그 데이터를 조작하거나 반환할 수 있습니다.

아래는 Person 클래스에 PrintInfo 멤버 함수를 추가한 예제입니다. 이 함수는 객체의 정보를 화면에 출력합니다.

class Person {
public:
    std::string name; // 사람의 이름
    int age; // 사람의 나이

    void PrintInfo() {
        std::cout << "이름: " << name << ", 나이: " << age << std::endl;
    }
};

예제 코드

이제 Person 클래스를 사용하는 간단한 예제 코드를 살펴보겠습니다.

int main() {
    // Person 클래스의 객체 생성
    Person person1;
    person1.name = "Alice";
    person1.age = 30;

    Person person2;
    person2.name = "Bob";
    person2.age = 25;

    // 객체의 정보 출력
    person1.PrintInfo();
    person2.PrintInfo();

    return 0;
}

[영진닷컴]그림으로 배우는 C++ Programming - 2nd Edition, 영진닷컴

위의 코드는 Person 클래스의 두 객체를 생성하고, 각 객체의 정보를 출력하는 예제입니다. PrintInfo 멤버 함수를 사용하여 객체의 정보를 출력할 수 있습니다.

friend 키워드의 사용

  1. friend 키워드를 사용하여 클래스 내부의 private 멤버에 다른 클래스나 함수에서 접근 권한을 부여할 수 있습니다.
  2. friend 함수는 클래스의 멤버 함수가 아니지만 해당 클래스의 private 및 protected 멤버에 접근할 수 있습니다.
  3. friend 선언은 해당 클래스의 정의 내에서 이루어지며, 다른 클래스나 함수를 friend로 지정합니다.

friend 예제 코드

다음은 friend 키워드를 사용한 간단한 예제 코드입니다. 여기서는 두 개의 클래스를 사용하여 friend 함수를 구현하고 클래스 간의 private 멤버에 접근합니다.

#include <iostream>

class B; // 클래스 B를 전방 선언

class A {
private:
    int privateValueA;

public:
    A() : privateValueA(0) {}

    // B 클래스를 friend로 선언하여 B의 private 멤버에 접근할 수 있게 함
    friend class B;

    void PrintValue() {
        std::cout << "A's privateValueA: " << privateValueA << std::endl;
    }
};

class B {
private:
    int privateValueB;

public:
    B() : privateValueB(0) {}

    void ModifyAValue(A &objA, int newValue) {
        objA.privateValueA = newValue;
    }

    void PrintAValue(A &objA) {
        std::cout << "B modifies A's privateValueA: " << objA.privateValueA << std::endl;
    }
};

int main() {
    A objA;
    B objB;

    objA.PrintValue(); // 초기값 출력

    objB.ModifyAValue(objA, 42); // B가 A의 privateValueA를 수정
    objB.PrintAValue(objA); // 수정된 값 출력

    return 0;
}

Accelerated C++:C++를 C++답게 배우는 16가지 방법, 한빛미디어

위의 코드에서 클래스 AB를 정의하고, B 클래스를 friend로 선언하여 B 클래스의 멤버 함수 ModifyAValueA 클래스의 private 멤버 privateValueA에 접근할 수 있게 합니다. 이를 통해 B 클래스는 A 클래스의 private 멤버를 수정하고 출력할 수 있습니다.

friend 키워드를 사용할 때는 주의해야 하며, 필요한 경우에만 사용해야 합니다. 클래스 간의 결합도를 느슨하게 유지하고, 정보 은닉과 객체 지향 원칙을 고려하여 friend를 사용하는 것이 좋습니다.

friend와 유사한 기능을 가지는 다른 키워드

protected 키워드: protected 키워드를 사용하면 상속 관계에서 부모 클래스의 멤버를 자식 클래스에서 접근할 수 있습니다. protected 멤버는 해당 클래스와 그 클래스를 상속하는 자식 클래스에서 접근 가능하며, 외부에서는 접근할 수 없습니다. 이것은 상속 관계에서의 정보 공유를 위한 방법으로 사용될 수 있습니다.

class Parent {
protected:
    int protectedValue;
};

class Child : public Parent {
public:
    void AccessParentProtected() {
        protectedValue = 42; // 부모 클래스의 protected 멤버에 접근
    }
};

friend 함수: 이미 friend 키워드에 대해 설명했지만, 다른 클래스의 private 멤버에 접근할 수 있도록 허용하는 데 사용됩니다.

class A {
private:
    int privateValue;

    friend void FriendFunction(A &objA);

public:
    A() : privateValue(0) {}
};

void FriendFunction(A &objA) {
    objA.privateValue = 42;
}

friend 클래스: 클래스 또한 다른 클래스를 friend로 선언할 수 있으며, 이로 인해 friend 클래스의 모든 멤버 함수가 접근 권한을 갖게 됩니다.

class A {
private:
    int privateValue;

    friend class FriendClass;

public:
    A() : privateValue(0) {}
};

class FriendClass {
public:
    void ModifyPrivateValue(A &objA) {
        objA.privateValue = 42;
    }
};

friend 함수 템플릿: C++11부터는 템플릿을 사용한 friend 함수를 정의할 수 있습니다. 이를 통해 특정 타입에 대한 friend 함수를 생성할 수 있습니다.

template <typename T>
class MyClass {
private:
    T privateValue;

    template <typename U>
    friend void FriendFunction(MyClass<U> &obj);

public:
    MyClass() : privateValue(0) {}
};

template <typename U>
void FriendFunction(MyClass<U> &obj) {
    obj.privateValue = 42;
}

이러한 기능성 키워드와 방식을 사용하여 C++에서 클래스 간의 접근 권한을 관리하고 정보 공유를 제어할 수 있습니다. 그러나 friend를 사용할 때는 주의해서 사용해야 하며, 객체 지향 설계 원칙과 의도를 고려하여 사용해야 합니다.

friend 함수와 보안 고려사항

friend 함수를 사용할 때 보안 측면에서 고려해야 할 주요 사항은 다음과 같습니다:

1. 정보 노출과 데이터 무결성

friend 함수를 사용하면 클래스의 private 멤버에 접근할 수 있으므로, 이를 악의적으로 활용하면 정보 노출과 데이터 무결성에 대한 위험이 증가할 수 있습니다. friend 함수를 지정할 때, 이러한 접근이 신중하게 제한되어야 합니다.

2. 캡슐화 깨짐

friend 함수를 남용하면 클래스의 캡슐화가 깨질 수 있습니다. 캡슐화는 클래스의 상세 내부 구현을 숨기고 외부에서 접근할 수 있는 인터페이스만 노출하는 것을 의미합니다. friend 함수로 인해 클래스의 private 멤버에 대한 직접적인 접근이 허용되면 캡슐화 원칙이 위배될 수 있습니다.

3. 최소한의 공유

friend 함수를 사용할 때는 최소한의 정보를 공유해야 합니다. 다른 클래스나 함수가 접근해야 하는 것은 정말로 필수적인 경우에만 friend로 지정하고, 가능한 한 접근 권한을 제한해야 합니다.

다음은 friend 함수를 사용할 때 보안을 고려한 예제 코드와 설명입니다:

class BankAccount {
private:
    double balance;

public:
    BankAccount(double initialBalance) : balance(initialBalance) {}

    friend void Withdraw(BankAccount &account, double amount);
};

void Withdraw(BankAccount &account, double amount) {
    if (amount > 0 && amount <= account.balance) {
        account.balance -= amount;
        std::cout << "Withdrawal successful. New balance: " << account.balance << std::endl;
    } else {
        std::cout << "Withdrawal failed. Insufficient funds." << std::endl;
    }
}

int main() {
    BankAccount myAccount(1000.0);
    Withdraw(myAccount, 500.0); // friend 함수로 잔액 변경 가능

    return 0;
}

C++ 함수형 프로그래밍:C++와 함수형 프로그래밍 패러다임의 만남, 에이콘출판

위의 코드에서 Withdraw 함수는 BankAccount 클래스의 private 멤버 balance에 접근하여 인출 작업을 수행합니다. 그러나 이 friend 함수는 최소한의 정보만 공유하고, 보안적으로 적절한 제한을 두어 데이터 무결성과 정보 은닉을 유지합니다.

요약하면, friend 함수를 사용할 때는 보안 측면에서 신중하게 고려해야 하며, 최소한의 정보만을 공유하고 데이터 무결성과 정보 은닉을 지키는 데 주의를 기울여야 합니다.

Friend 클래스와 비멤버 함수의 활용 예제

friend 키워드는 다음과 같은 상황에서 유용하게 활용됩니다:

1. 데이터 은닉 유지

friend 키워드를 사용하여 클래스 간에 특정 정보를 공유하면서도 데이터 은닉을 유지할 수 있습니다. 예를 들어, 두 개의 클래스가 있을 때 한 클래스의 private 멤버에 다른 클래스에서 접근해야 할 때 friend를 사용합니다.

class B; // 클래스 B를 전방 선언

class A {
private:
    int privateValueA;

public:
    A() : privateValueA(0) {}

    // B 클래스를 friend로 선언하여 B의 private 멤버에 접근할 수 있게 함
    friend class B;

    void PrintValue() {
        std::cout << "A's privateValueA: " << privateValueA << std::endl;
    }
};

class B {
private:
    int privateValueB;

public:
    B() : privateValueB(0) {}

    void ModifyAValue(A &objA, int newValue) {
        objA.privateValueA = newValue; // A 클래스의 private 멤버에 접근 및 수정
    }

    void PrintAValue(A &objA) {
        std::cout << "B modifies A's privateValueA: " << objA.privateValueA << std::endl;
    }
};

2. 연산자 오버로딩

friend 함수는 연산자 오버로딩에 사용될 수 있습니다. 클래스 외부에서 연산자를 정의하고 클래스의 private 멤버에 접근하기 위해 friend 함수를 사용합니다.

class Complex {
private:
    double real;
    double imaginary;

public:
    Complex(double r, double i) : real(r), imaginary(i) {}

    // + 연산자 오버로딩
    friend Complex operator+(const Complex &c1, const Complex &c2);

    void Display() {
        std::cout << real << " + " << imaginary << "i" << std::endl;
    }
};

// + 연산자 오버로딩을 위한 friend 함수 정의
Complex operator+(const Complex &c1, const Complex &c2) {
    return Complex(c1.real + c2.real, c1.imaginary + c2.imaginary);
}

3. 유틸리티 클래스

friend 클래스는 유틸리티 클래스로 활용될 수 있습니다. 특정 클래스의 private 멤버에 접근하여 작업을 수행하는 클래스를 정의할 때 사용됩니다.

class MyString {
private:
    char *str;
    int length;

public:
    MyString(const char *s) : length(strlen(s)) {
        str = new char[length + 1];
        strcpy(str, s);
    }

    friend class StringUtility; // StringUtility 클래스를 friend로 선언

    void Display() {
        std::cout << str << std::endl;
    }

    ~MyString() {
        delete[] str;
    }
};

class StringUtility {
public:
    static void Uppercase(MyString &myStr) {
        for (int i = 0; i < myStr.length; ++i) {
            myStr.str[i] = toupper(myStr.str[i]);
        }
    }
};

int main() {
    MyString myStr("Hello, World!");
    myStr.Display();

    StringUtility::Uppercase(myStr); // StringUtility 클래스로 MyString의 private 멤버에 접근
    myStr.Display();

    return 0;
}

아무도 알려주지 않은 C++ 코딩의 기술, 로드북

위의 예제 코드에서는 StringUtility 클래스가 MyString 클래스의 private 멤버에 접근하여 문자열을 대문자로 변경하는 역할을 합니다.

이러한 예제 코드를 통해 friend 키워드가 어떤 상황에서 사용되며, 클래스 간의 정보 공유와 데이터 은닉을 유지하는 데 어떻게 도움을 주는지를 더 자세히 이해할 수 있습니다.

결론

friend 함수를 사용할 때 보안 측면에서의 주요 결론은 신중함과 최소한의 정보 공유가 핵심입니다. friend 함수를 지정할 때는 데이터 무결성과 정보 은닉을 지키기 위해 제한을 두어야 합니다. 클래스 간의 접근 권한을 너무 넓게 허용하면 정보 노출과 데이터 무결성 문제가 발생할 수 있습니다. 캡슐화를 깨뜨리지 않도록 주의하며, friend 함수의 사용을 최소화하여 클래스의 캡슐화를 유지하는 것이 중요합니다. 핵심은 보안과 캡슐화를 함께 고려하여 객체 지향 설계를 진행하는 것이며, 필요한 경우에만 friend를 사용하여 클래스 간의 정보 공유를 관리해야 합니다.

이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.