나만의 작은 도서관

[C++][Class] 클래스 관련 기타 내용(explicit, mutable, friend 키워드 등...) 본문

C++/문법 및 메소드(STL)

[C++][Class] 클래스 관련 기타 내용(explicit, mutable, friend 키워드 등...)

pledge24 2025. 3. 26. 02:19

explicit 키워드

explicit Mystring(int capacity); // 생성자를 explicit으로 지정
  • 대입 연산자가 생성자로 암시적 변환되지 못하도록 막는 키워드.

 

암시적 변환(implicit conversion) 시 문제점

  • 암시적 변환프로그래머가 명시적으로 변환을 요청하지 않더라도 컴파일러가 자동으로 처리하는 타입 변환을 말한다. 
  • 이러한 암시적 변환은 아래의 예시에서 대입 연산자가 생성자로 변환되는 것처럼, 원하지 않는 방식으로 변환되는 문제가 있다.
class MyClass {
public:
    MyClass(int x) { cout << "MyClass Constructor" << '\\n';}
};

void func(MyClass myclass) { cout << "func" << '\\n';}

// [MyClass myclass = 3] => [MyClass myclass(3)]으로 암시적 변환 발생
func(3); // func(MyClass(3))이 실행.

// 실행 결과
// MyClass Constructor
// func
  • 이러한 암시적 변환을 막기 위해 생성자 앞에 explicit을 추가하여 대입 연산자가 생성자로 변환되는 것을 막는다.
    • 참고로 explicit으로 지정한 생성자만 암시적 변환이 막히기 때문에, explicit을 선언하지 않은 생성자로는 암시적 변환이 될 수 있다.
      => 암시적 변환을 금지하려면 모든 생성자를 explicit으로 지정해야 한다!
class MyClass {
public:
    /* explicit 선언 */
    explicit MyClass(int x) { cout << "MyClass Constructor" << '\\n';}
};

// [MyClass myclass = 3] => [MyClass myclass(3)]으로 암시적 변환 금지됨
void func(MyClass myclass) { cout << "func" << '\\n';}

int main(void){
    func(3); // 컴파일 오류: "int"에서 "MyClass"(으)로 변환하기 위한 적절한 생성자가 없습니다.
    return 0;
}

 

 


mutable 키워드

mutable int data_;
  • 상수 함수에서 멤버 변수의 값을 수정할 수 있도록 바꿔주는 키워드.

 

mutable 키워드를 사용하는 이유

  • 상수 함수는 “이 함수는 객체의 내부 상태에 영향을 주지 않습니다.”라는 의도를 가지고 있다. 즉, 해당 함수가 읽기 작업만을 수행한다는 의도를 명시할 수 있다.
  • 하지만 아주 낮은 빈도로 일부 변수의 값을 변경해야 하는 작업이 있을 수 있는데, 이러한 작업을 수행하기 위해서는 함수를 상수 함수로 지정할 수 없다.
    • 참고로 상수 함수에서 호출하는 함수 또한 쓰기 작업을 할 수 없다.(호출 함수가 상수 함수가 아니어도)
  • 하지만 mutable 키워드는 사용하면 상수 함수로 유지하면서 mutable로 지정한 멤버 변수에 대해 쓰기 작업을 할 수 있으므로, 위와 같은 문제를 해결할 수 있다.
class Cache {
public:
    void UpdateCache(const int data) const {
        _data = data;  // mutable 이므로 수정 가능!
    }
    void GetData(const int data) const { // 상수 함수 정의
        if(data == _data)
            cout << "cache Hit! " << '\\n';
        else{
            cout << "cache Miss... " << '\\n';
            UpdateCache(data); // 데이터 갱신
        }

    }

private:
    mutable int _data = -1; // 초기값 -1
};

int main() {
    Cache cache; // 크기가 1인 캐시
    cache.GetData(10); // cache Miss...
    cache.GetData(10); // cache Hit!
}

 


friend 키워드

friend class ClassName;
friend void func(int x);
  • friend 키워드는 지정한 대상에게 private 또는 protected 멤버 변수와 함수에 접근할 수 있도록 허용하는 키워드이다.
  • 지정된 대상은 해당 클래스의 private 멤버 변수에 접근할 수 있지만, 반대로 지정된 대상의 private 멤버 변수는 해당 클래스가 접근할 수 없다.
    • A가 B를 friend로 지정 시
      • B → A 접근 (O)
      • A → B 접근 (X)
  • friend 키워드의 주목적은 일부 대상만 private 멤버 변수에 접근할 수 있도록 하기 위함이다.
#include <iostream>

using namespace std;

class A 
{
public:
    /* 멤버 "B::privateKey"에 액세스할 수 없습니다. */
    // void showOtherKey(const B& other) {cout << other.privateKey << '\\n';}
private:
    friend class B;
    string privateKey = "A's private Key";
};

class B 
{
public:
    void showOtherKey(const A& other) {cout << other.privateKey << '\\n';}
private:
    string privateKey = "B's private Key";
};

int main(void){
    A a;
    B b;
    
    b.showOtherKey(a); // 실행 결과: A's private
}

 


abstract과 sealed 키워드

virtual void func2() abstract;
virtual void func2() sealed;
  • abstract과 sealed 키워드는 C++ 표준 키워드가 아닌 Visual Studio (MSVC)에서 확장 기능으로 제공하는 키워드이다.
  • abstract 키워드는 C++ 표준의 순수 가상 함수 표현인 ‘ = 0’ 역할을 하며, 클래스 이름 뒤나 함수 이름 뒤에 붙일 수 있다.
    • 클래스 이름 뒤에 붙이는 abstract는 추상 클래스임을 알리기 위한 가독성용일 뿐 기능은 없다.
  • sealed 키워드는 C++ 표준의 재정의를 금지하는 final 키워드 역할을 한다.
  • 결론적으로 abstract, sealed 둘 다 기능적인 면에선 C++ 표준으로 대체가 되는 키워드들이므로, 가독성 면에서만 장점이 있다.

 


멤버 클래스(Member Class)

  • 멤버 클래스는 클래스의 멤버 변수로 존재하는 클래스를 의미한다.
  • 멤버 클래스는 포함 관계(has-a)에 해당하는 클래스이다.
class Engine {
public:
    Engine() { std::cout << "Engine 생성\\n"; }
};

class Car {
    Engine engine; // Engine이 Car의 멤버 클래스
public:
    Car() { std::cout << "Car 생성\\n"; }
};

 


중첩 클래스(Nested Class)

  • 중첩 클래스는 어떤 클래스의 내부에 정의된 클래스를 의미한다.
  • 중첩 클래스는 외부 클래스와 논리적으로 강한 관계가 있는 클래스를 그룹화하여 코드의 가독성을 높이고, 외부에서 직접적인 접근을 제한하기 위해 사용한다.
  • 중첩 클래스를 사용하는 사례로 연결 리스트, 트리 등의 자료 구조가 있다.
#include <iostream>

class Outer {
private:
    int outerValue;

public:
    Outer(int v) : outerValue(v) {}

    // 중첩 클래스 정의
    class Inner {
    public:
        void show() {
            std::cout << "Inner class method" << std::endl;
        }
    };
};

int main() {
    // 중첩 클래스 객체 생성
    Outer::Inner innerObj;
    innerObj.show();  // 출력: Inner class method

    return 0;
}

 


참고 자료

https://stackoverflow.com/questions/1298093/can-i-use-abstract-keyword-in-c-class

https://modoocode.com/253