C++ 파생 클래스 멤버의 식별 및 액세스 - 범위 구분자

파생 클래스에서 멤버는 액세스 속성에 따라 다음과 같은 네 가지 유형으로 나눌 수 있습니다.
(1) 액세스할 수 없는 멤버 . 이들은 기본 클래스의 전용 멤버에서 상속되며 파생 클래스나 파생 클래스 개체를 생성하는 모듈은 여기에 액세스할 수 없으며 새 클래스가 파생 클래스에서 파생되는 경우에도 액세스할 수 없습니다.
(2) 개인 회원 . 바탕 클래스에서 상속받은 멤버와 새롭게 추가된 멤버를 포함하여 파생 클래스 내부에서는 접근이 가능하나 파생 클래스 객체를 생성하는 모듈은 접근이 불가능하며, 파생이 계속되면 새로운 파생 클래스에서는 접근 불가 멤버가 됨 .
(3) 회원을 보호합니다 . 새로 추가되거나 기본 클래스에서 상속될 수 있습니다. 파생 클래스의 내부 멤버는 액세스할 수 있지만 파생 클래스 개체를 생성하는 모듈은 액세스할 수 없습니다. 추가 파생은 전용 멤버 또는 보호 멤버가 될 수 있습니다. 새로운 파생 클래스에서.
(4) 일반 회원 . 파생 클래스와 파생 클래스 개체를 생성하는 모듈은 액세스할 수 있고 계속해서 파생될 수 있으며, 새로 파생된 클래스의 전용 또는 보호 멤버일 수 있습니다.

파생 클래스 방문에서는 고유하게 식별할 수 있는 보이는 멤버만 방문할 수 있습니다. 하나의 식에서 둘 이상의 멤버를 참조할 수 있는 경우 이를 모호하다고 합니다.

1. 범위 지정자

범위 구분자는 ::액세스할 멤버가 상주하는 클래스의 이름을 제한하는 데 사용할 수 있는 공통 " "입니다. 일반적인 사용 형태는 다음과 같습니다.

类名::成员名//数据成员
类名::成员名(参数表)//函数成员

2. 범위 판별자는 클래스 패밀리 계층 구조에서 멤버를 고유하게 식별합니다.

서로 다른 범위에서 선언된 식별자의 경우 가시성 원칙은 다음과 같습니다. 내포 관계가 있는 범위가 두 개 이상인 경우 외부 계층에서 식별자를 선언하고 내부 계층에서 동일한 이름의 식별자를 다시 선언하지 않으면 외부 계층에서 같은 이름의 식별자를 선언한 경우 계층 식별자는 여전히 내부 계층에 표시되며, 동일한 이름의 식별자가 내부 계층에 선언된 경우 외부 계층 식별자는 내부 계층에 표시되지 않습니다. 내부 레이어 식별자가 같은 이름의 외부 레이어 식별자를 숨기는 현상을 숨겨진 규칙 이라고 합니다 .

클래스의 파생 계층 구조에서 기본 클래스의 멤버와 파생 클래스에서 추가한 멤버 모두 범위가 있습니다. 둘의 행동 범위가 다른데, 서로를 포함하는 두 계층이고 파생 클래스가 내부 계층입니다. 이때 파생클래스가 바탕클래스의 멤버와 같은 이름의 새로운 멤버를 선언하면 파생클래스의 새로운 멤버는 바깥 바탕클래스에서 같은 이름의 멤버를 숨기고 멤버 이름을 직접 사용한다. 파생 클래스의 멤버에만 액세스할 수 있습니다. 기본 클래스의 멤버 함수와 같은 이름을 가진 새로운 함수가 파생 클래스에 선언되면 기본 클래스에서 상속된 같은 이름을 가진 함수의 모든 오버로드된 형태는 함수의 매개 변수 목록이 있어도 숨겨집니다. 다른. . 숨겨진 멤버에 액세스하려면 범위 지정자와 기본 클래스 이름을 사용하여 한정해야 합니다.

다중 상속의 경우 먼저 각 기본 클래스 간에 상속 관계가 있는지, 공통 기본 클래스가 없는지 여부를 고려하십시오. 가장 고전적인 경우는 모든 기본 클래스에 우수한 기본 클래스가 없는 경우입니다. 파생 클래스의 여러 기본 클래스에 이름이 같은 멤버가 있고 동시에 파생 클래스가 이름이 같은 멤버를 추가하는 경우 파생 클래스 멤버는 이름이 같은 모든 기본 클래스 멤버를 숨깁니다. . 이때 "객체명.멤버명" 또는 "객체포인터->멤버명"을 사용하여 파생클래스의 새로운 멤버를 고유하게 식별하고 접근하며, 동일한 이름을 가진 바탕클래스의 멤버들도 다음을 이용하여 접근이 가능하다. 기본 클래스 이름 및 범위 식별자. 그러나 파생 클래스가 같은 이름의 멤버를 선언하지 않으면 "객체명.멤버명" 또는 "객체포인터 -> 멤버명"을 이용하여 해당 멤버를 고유하게 식별할 수 없다. 이때 서로 다른 기본 클래스에서 상속받은 멤버는 이름과 범위가 같으며, 이 경우 기본 클래스 이름과 범위 식별자로 멤버를 식별해야 합니다.

[예제] 베이스 클래스 B1, B2를 정의하고, 베이스 클래스 B1, B2의 공통 공개 파생에서 새로운 클래스 D를 생성합니다. 데이터 멤버 v와 fun 함수는 모두 두 개의 기본 클래스에서 선언되고 동일한 이름을 가진 두 멤버가 파생 클래스에 추가됩니다. 이때 D반은 총 6명의 멤버로 구성되어 있으며, 이 6명의 멤버는 이름이 2개뿐이다.

#include<iostream>
using namespace std;

class B1//定义基类B1
{
    
    
public:
	int v;
	void fun()
	{
    
    
		cout << "基类B1的成员" << endl;
	}
};

class B2//定义基类B2
{
    
    
public:
	int v;
	void fun()
	{
    
    
		cout << "基类B2的成员" << endl;
	}
};

class D :public B1, public B2//定义派生类D
{
    
    
public:
	int v;//同名数据成员
	void fun()//同名函数成员
	{
    
    
		cout << "派生类D的成员" << endl;
	}
};

int main()
{
    
    
	D d;
	D* p = &d;

	d.v = 1;//对象名.成员名标识
	d.fun();//D类对象d访问D类成员函数fun

	d.B1::v = 2;//作用域分辨符标识
	d.B1::fun();//D类对象d访问B1类成员函数fun

	p->B2::v = 3;//作用域分辨符标识
	p->B2::fun();//D类对象d访问B2类成员函数fun

	return 0;
}

여기에 이미지 설명 삽입
메인 함수에서 파생 클래스 D의 객체 d를 생성하는데, 히든 룰에 따르면 멤버 이름을 통해 이 클래스의 멤버에 접근하면 파생 클래스의 새 멤버 두 개에만 접근할 수 있다. 기본 클래스에서 상속됨 ​​멤버는 외부 범위로 인해 숨겨집니다. 이때 기본 클래스에서 상속된 멤버에 액세스하려면 클래스 이름과 범위 지정자를 사용해야 합니다.

main 함수의 다음 두 세트의 명령문:

	d.B1::v = 2;//作用域分辨符标识
	d.B1::fun();//D类对象d访问B1类成员函数fun

	p->B2::v = 3;//作用域分辨符标识
	p->B2::fun();//D类对象d访问B2类成员函数fun

기본 클래스 B1과 B2에서 각각 상속된 멤버에 액세스하는 것입니다. 범위 판별자를 통해 파생 클래스에서 기본 클래스에서 상속된 멤버를 명확하고 고유하게 식별하여 액세스 목적을 달성하고 멤버가 숨겨지는 문제를 해결합니다.

위의 예에서 파생 클래스가 기본 클래스와 동일한 이름을 가진 멤버를 선언하지 않으면 "객체 이름. 멤버 이름"을 사용하면 모든 멤버, 클래스 B1 및 클래스에서 이름이 같은 멤버에 액세스할 수 없습니다. B2는 동일한 범위를 가지며 시스템은 고유하게 식별할 수 없습니다. 범위 식별자를 사용해야 합니다. 위 예제의 파생 클래스를 다음 형식으로 변경합니다.

class D :public B1, public B2//定义派生类D
{
    
    };

프로그램의 나머지 부분이 변경되지 않은 경우 기본 기능의 "개체 이름. 멤버 이름"에 대한 액세스 방법이 잘못됩니다. 및 사용법 이 모호하지 않도록
여기에 이미지 설명 삽입
하려면 using 키워드를 사용하여 명확히 할 수 있습니다. 예를 들어: d.v = 1;d.fun();

class D :public B1, public B2//定义派生类D
{
    
    
public:
	using B1::v;
	using B1::fun;
};

이러한 방식으로 주 함수의 d.v = 1;합계 는 d.fun();B1의 관련 멤버에 대한 참조를 명확하게 나타낼 수 있습니다.

using의 일반적인 기능은 한 범위의 이름을 다른 범위에 도입하는 것입니다. 또한 매우 유용한 사용법이 있습니다: 기본 클래스의 함수 이름에 using을 사용하여 파생 클래스가 동일한 이름을 정의하지만 다른 매개변수를 정의하는 경우 기본 클래스의 함수는 숨겨지지 않고 두 개의 오버로드된 함수가 파생 클래스의 범위에서 공존합니다. 예를 들어:

#include<iostream>
using namespace std;

class B1//定义基类B1
{
    
    
public:
	int v;
	void fun()
	{
    
    
		cout << "基类B1的成员" << endl;
	}
};

class D2 :public B1
{
    
    
public:
	using B1::fun;
	void fun(int i)
	{
    
    
		cout << i << endl;
	}
};

int main()
{
    
    
	D2 dd;
	dd.fun();
	dd.fun(5);

	return 0;
}

작업 결과:

여기에 이미지 설명 삽입
이때, D2의 객체를 이용하여 베이스 클래스 B1에서 파라미터 없이 fun 함수를 직접 호출하거나, 파생 클래스 D2에서 int 파라미터로 fun 함수를 직접 호출할 수 있다.

파생 클래스의 직접 기본 클래스 중 일부 또는 전부가 다른 공통 기본 클래스에서 파생된 경우 이러한 직접 기본 클래스에서 상위 기본 클래스에서 상속된 멤버의 이름이 같으므로 파생 클래스의 현상 동일한 이름이 클래스에서도 발생하며 이 형식의 동일한 이름의 멤버도 범위 구분자에 의해 고유하게 식별되어야 하며 직접 기본 클래스에 의해 한정되어야 합니다.

[예제] 데이터 멤버 v0과 함수 멤버 fun0을 선언한 베이스 클래스 B0이 있고, 클래스 B1과 B2는 B0에서 파생되고, 새로운 클래스 D는 B1과 B2에서 베이스 클래스로 파생됩니다. 파생 클래스에는 같은 이름을 가진 새로운 멤버가 추가되지 않으며, 이때 클래스 D는 B1과 B2에서 상속받은 베이스 클래스 B0에 같은 이름을 가진 멤버 v0과 fun0을 포함합니다.

class B0
{
    
    
public:
	int v0;
	void fun0()
	{
    
    
		cout << "基类B0的成员" << endl;
	}
};
class B1 :public B0
{
    
    
public:
	int v1;
 };
class B2 :public B0
{
    
    
public:
	int v2;
};
class D :public B1, public B2
{
    
    
public:
	int v;
	void fun()
	{
    
    
		cout << "基类D的成员" << endl;
	}
};

int main()
{
    
    
	D d;
	d.B1::v0 = 2;
	d.B1::fun0();
	d.B2::v0 = 3;
	d.B2::fun0();
	return 0;
}

실행 결과:
여기에 이미지 설명 삽입
분석:
메인 함수에서 파생 클래스 D의 개체 d가 생성되고 이 클래스의 멤버 v0 및 fun0이 멤버 이름을 통해서만 액세스되는 경우 시스템에서 참조할 멤버를 고유하게 결정할 수 없습니다. 이때, 직접 베이스 클래스 이름을 통해 접근할 베이스 클래스로부터 상속받은 멤버를 결정하기 위해서는 범위 판별자를 사용해야 한다.

이 경우 파생 클래스의 개체는 동시에 메모리에 멤버 v0의 동일한 이름의 두 복사본을 가집니다. 아래 그림과 같이:
여기에 이미지 설명 삽입

데이터 멤버의 경우 각각 B0~B1, B2의 생성자를 호출하여 두 개의 v0을 초기화할 수 있지만 서로 다른 값을 저장할 수 있고 범위 판별자를 사용하여 직접 기본 클래스 이름 한정을 통해 별도로 액세스할 수도 있지만 많은 이 경우 하나의 데이터 사본만 필요합니다. 동일한 멤버의 여러 복사본은 메모리 오버헤드를 증가시킵니다. C++는 이 문제를 해결하기 위해 가상 기본 클래스 기술을 제공합니다.

[참고] 위의 예에서 사실 B0 클래스의 함수 멤버 fun0()의 코드 사본은 항상 하나만 존재하는데 fun0 함수를 호출하는 이유는 여전히 기본 클래스 이름 B1로 한정되어야 합니다. 그리고 B2는 non-static 멤버 함수를 항상 호출하기 때문입니다. 위의 예에서 클래스 D의 객체에는 클래스 B0의 두 하위 객체가 있으므로 fun0 함수를 호출할 때 B1과 B2에 의해 제한되어야 호출할 B0 객체를 명확하게 대상으로 지정할 수 있습니다.

추천

출처blog.csdn.net/NuYoaH502329/article/details/132134208