멤버 액세스 제어(C++)

액세스 제어를 사용하면 클래스의 인터페이스를 구현 세부 정보 및 파생 클래스에서 private 만 사용할 멤버와 protected 구분 public 할 수 있습니다. 액세스 지정자는 다음 액세스 지정자가 나타날 때까지 해당 액세스 지정자 뒤에 선언된 모든 멤버에 적용됩니다.

class Point
{
public:
    Point( int, int ) // Declare public constructor.;
    Point();// Declare public default constructor.
    int &x( int ); // Declare public accessor.
    int &y( int ); // Declare public accessor.

private:                 // Declare private state variables.
    int _x;
    int _y;

protected:      // Declare protected function for derived classes only.
    Point ToWindowCoords();
};

기본 액세스는 private 클래스와 public 구조체 또는 공용 구조체에 있습니다. 클래스의 액세스 지정자는 순서에 관계없이 여러 번 사용할 수 있습니다. 클래스 형식의 개체에 대한 스토리지 할당은 구현에 따라 달라집니다. 그러나 컴파일러는 액세스 지정자 간에 연속적으로 더 높은 메모리 주소에 멤버 할당을 보장해야 합니다.

멤버 Access Control

액세스 형식 의미
private 클래스로 private 선언된 클래스 멤버는 클래스의 멤버 함수 및 친구(클래스 또는 함수)에서만 사용할 수 있습니다.
protected 클래스의 멤버 함수 및 친구(클래스 또는 함수)에서 사용할 수 있는 것으로 protected 선언된 클래스 멤버입니다. 또한 클래스에서 파생 클래스에서 사용할 수 있습니다.
public 모든 함수에서 사용할 수 있는 것으로 public 선언된 클래스 멤버입니다.

액세스 제어를 사용하면 개체를 사용할 수 없는 방식으로 개체를 사용할 수 없습니다. 이 보호는 명시적 형식 변환(캐스트)을 수행할 때 손실됩니다.

참고 항목

액세스 제어는 모든 이름(멤버 함수, 멤버 데이터, 중첩 클래스 및 열거자)에 동일하게 적용 가능합니다.

파생 클래스의 Access Control

파생 클래스에서 액세스할 수 있는 기본 클래스의 멤버는 두 가지 요인으로 인해 결정됩니다. 이와 동일한 요인이 파생 클래스의 상속된 멤버에 대한 액세스도 결정합니다.

  • 파생 클래스가 액세스 지정자를 사용하여 기본 클래스를 public 선언하는지 여부입니다.

  • 기본 클래스의 멤버 액세스

다음 표에서는 이러한 요인과 기본 클래스 멤버 액세스를 확인하는 방법 간의 상호 작용을 보여 줍니다.

기본 클래스의 멤버 액세스

private protected public
파생 액세스에서 항상 액세스할 수 없음 private 파생 클래스에서 파생을 사용하는 private 경우 private 파생 클래스에서 파생을 사용하는 private 경우
protected 파생 클래스에서 파생을 사용하는 protected 경우 protected 파생 클래스에서 파생을 사용하는 protected 경우
protected 파생 클래스에서 파생을 사용하는 public 경우 public 파생 클래스에서 파생을 사용하는 public 경우

다음 예제에서는 액세스 파생을 보여 줍니다.

// access_specifiers_for_base_classes.cpp
class BaseClass
{
public:
    int PublicFunc(); // Declare a public member.
protected:
    int ProtectedFunc(); // Declare a protected member.
private:
    int PrivateFunc(); // Declare a private member.
};

// Declare two classes derived from BaseClass.
class DerivedClass1 : public BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

class DerivedClass2 : private BaseClass
{
    void foo()
    {
        PublicFunc();
        ProtectedFunc();
        PrivateFunc(); // function is inaccessible
    }
};

int main()
{
    DerivedClass1 derived_class1;
    DerivedClass2 derived_class2;
    derived_class1.PublicFunc();
    derived_class2.PublicFunc(); // function is inaccessible
}

에서 DerivedClass1멤버 함수 PublicFuncpublic 멤버이며 ProtectedFuncprotected 기본 클래스이므로 멤버 BaseClass 입니다 public . PrivateFuncprivate 파생 BaseClass클래스에 액세스할 수 없습니다.

에서 DerivedClass2함수는 PublicFuncProtectedFunc 기본 클래스이므로 멤버 BaseClassprivate 간주됩니다private. 다시 말하지만, PrivateFuncprivateBaseClass파생 클래스에는 액세스할 수 없습니다.

파생 클래스는 기본 클래스 액세스 지정자 없이 선언할 수 있습니다. 이러한 경우 파생 클래스 선언에서 키워드(keyword) 사용하는 경우 파생이 class 고려 private 됩니다. 파생 클래스 선언에서 public 키워드(keyword) 사용하는 경우 파생이 struct 고려됩니다. 예를 들어, 다음 코드는

class Derived : Base
...

다음과 동일합니다.

class Derived : private Base
...

마찬가지로, 다음 코드는

struct Derived : Base
...

다음과 동일합니다.

struct Derived : public Base
...

액세스 권한이 있다고 private 선언된 멤버는 해당 함수 또는 클래스가 기본 클래스의 선언을 사용하여 friend 선언되지 않는 한 함수 또는 파생 클래스에 액세스할 수 없습니다.

형식에는 union 기본 클래스가 있을 수 없습니다.

참고 항목

기본 클래스를 private 지정할 때 파생 클래스의 사용자가 멤버 액세스를 이해할 수 있도록 키워드(keyword) 명시적으로 사용하는 private 것이 좋습니다.

액세스 제어 및 정적 멤버

기본 클래스를 지정 private하면 비정적 멤버에만 영향을 줍니다. 파생 클래스에서 계속 공용 정적 멤버에 액세스할 수 있습니다. 그러나 포인터, 참조 또는 개체를 사용하여 기본 클래스의 멤버에 액세스하려면 다시 액세스 제어를 적용하는 변환이 필요할 수 있습니다. 다음 예제를 참조하세요.

// access_control.cpp
class Base
{
public:
    int Print();             // Nonstatic member.
    static int CountOf();    // Static member.
};

// Derived1 declares Base as a private base class.
class Derived1 : private Base
{
};

// Derived2 declares Derived1 as a public base class.
class Derived2 : public Derived1
{
    int ShowCount();    // Nonstatic member.
};

// Define ShowCount function for Derived2.
int Derived2::ShowCount()
{
    // Call static member function CountOf explicitly.
    int cCount = ::Base::CountOf();     // OK.

    // Call static member function CountOf using pointer.
    cCount = this->CountOf();  // C2247: 'Base::CountOf'
                               // not accessible because
                               // 'Derived1' uses 'private'
                               // to inherit from 'Base'
    return cCount;
}

위 코드에서는 액세스 제어가 Derived2에 대한 포인터를 Base에 대한 포인터로 변환하지 못하도록 합니다. this 포인터는 암시적으로 형식Derived2 *입니다. 함수 thisCountOf 선택하려면 형식Base *으로 변환해야 합니다. 이러한 변환은 간접 기본 클래스Derived2이므로 private 허용되지 Base 않습니다. 기본 클래스 형식으로 private 의 변환은 직접 파생 클래스에 대한 포인터에 대해서만 허용됩니다. 따라서 형식의 포인터를 형식 Derived1 *Base *으로 변환할 수 있습니다.

포인터, 참조 또는 개체를 CountOf 사용하여 함수를 선택하지 않고 함수에 대한 명시적 호출은 변환을 의미하지 않습니다. 이것이 바로 호출이 허용되는 이유입니다.

파생 클래스T의 멤버 및 친구는 포인터를 포인터로 변환하여 T 직접 기본 클래스T로 private 변환할 수 있습니다.

가상 함수에 대한 액세스

함수에 적용된 virtual 액세스 제어는 함수를 호출하는 데 사용되는 형식에 따라 결정됩니다. 함수 선언을 재정의해도 지정된 형식에 대한 액세스 제어에는 영향을 주지 않습니다. 예시:

// access_to_virtual_functions.cpp
class VFuncBase
{
public:
    virtual int GetState() { return _state; }
protected:
    int _state;
};

class VFuncDerived : public VFuncBase
{
private:
    int GetState() { return _state; }
};

int main()
{
   VFuncDerived vfd;             // Object of derived type.
   VFuncBase *pvfb = &vfd;       // Pointer to base type.
   VFuncDerived *pvfd = &vfd;    // Pointer to derived type.
   int State;

   State = pvfb->GetState();     // GetState is public.
   State = pvfd->GetState();     // C2248 error expected; GetState is private;
}

앞의 예제에서 호출 형식 VFuncBase 에 대한 포인터를 사용하여 가상 함수 GetState 를 호출VFuncDerived::GetState하면 GetState 다음과 같이 public처리됩니다. 그러나 형식 VFuncDerived 에 대한 포인터를 사용하여 호출 GetState 하는 것은 클래스VFuncDerived에서 선언되므로 private 액세스 제어 위반 GetState 입니다.

주의

가상 함수 GetState는 기본 클래스 VFuncBase에 대한 포인터를 사용하여 호출할 수 있습니다. 그렇다고 해서 호출된 함수가 해당 함수의 기본 클래스 버전이라는 의미는 아닙니다.

다중 상속으로 액세스 제어

가상 기본 클래스를 사용하는 다중 상속 격자의 경우 지정된 이름은 둘 이상의 경로를 통해 도달할 수 있습니다. 이렇게 다양한 경로에는 다양한 액세스 제어가 적용될 수 있기 때문에 컴파일러는 최적의 액세스를 제공하는 경로를 선택합니다. 다음 그림을 참조하세요.

Diagram showing access along the paths of an inheritance graph.

다이어그램은 다음과 같은 상속 계층 구조를 보여 줍니다. 클래스 VBase는 기본 클래스입니다. 클래스 LeftPath는 가상 private VBase를 사용하여 VBase에서 상속됩니다. 클래스 RightPath는 VBase에서 상속되지만 가상 public VBase를 사용합니다. 마지막으로, 클래스 Derived는 LeftPath, RightPath를 사용하는 public 클래스 LeftPath 및 클래스 RightPath public 모두에서 상속됩니다.

상속 그래프의 경로를 따라 액세스

그림에서 클래스 VBase에 선언된 이름은 언제나 클래스 RightPath를 통해 도달됩니다. 기본 클래스로 선언하고 .로 public 선언하므로 VBase 올바른 경로에 더 쉽게 액세스할 수 RightPath 있습니다 VBaseprivate.LeftPath

참고 항목

C++ 언어 참조