C++의 상속/가상 상속 원칙

C++의 상속

1. 상속의 개념과 정의

상속은 코드 재사용률을 높이는 중요한 방법입니다.프로그래머가 원래 클래스의 특성을 유지하면서 다른 기능을 추가할 수 있습니다 .이러한 클래스를 파생 클래스라고 합니다. 상속은 클래스 설계 수준을 재사용하는 것입니다 .

class Person
{
public:
	void Print()
	{
		cout << "name: " << _name << endl;
		cout << "age: " << _age << endl;
	}
	
protected:
	string _name = "牡丹";
	int _age = 18;
};

class Student :public Person
{
protected:
	int _stuid;
};

class Teacher :public Person
{
protected:
	int _jobid;
};

int main(void)
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

작업 결과는 다음과 같습니다.

분명히 Student和Teacher거기에는 기능이 없습니다 Print(). 왜 여전히 정상적으로 실행됩니까? 그들은 모두 Person클래스를 상속하기 때문입니다.

1.1 상속 정의

아래에서 Person이 기본 클래스라고도 하는 상위 클래스임을 알 수 있습니다. Student는 파생 클래스라고도 하는 하위 클래스입니다.

1.12 상속 관계 및 액세스 한정자

클래스 멤버/상속 공공 상속 보호된 상속 개인 상속
기본 클래스의 공개 멤버 파생 클래스의 공개 멤버 파생 클래스의 보호된 멤버 파생 클래스의 전용 멤버
기본 클래스의 보호된 멤버 파생 클래스의 보호된 멤버 파생 클래스의 보호된 멤버 파생 클래스의 전용 멤버
기본 클래스의 전용 멤버 파생 클래스에서 보이지 않음(숨김) 파생 클래스에서 보이지 않음(숨김) 파생 클래스에서 보이지 않음(숨김)

요약하다

1. 바탕 클래스의 멤버는 private상속 방식과 관계없이 파생 클래스에서는 보이지 않고 숨겨지며, 클래스 외부에서는 private파생 클래스에서 호출할 수 없는 멤버로 숨겨진다.

2. private멤버는 파생 클래스에서는 접근할 수 없지만, 클래스 외부에서는 접근하지 못하게 하고 싶다면 파생 클래스에서 접근하면 사용할 수 있다 protected.

3. public > protected > private하위 클래스에서 기본 클래스의 다른 구성원의 액세스 방법 == 권한이 적은 것

4. 키워드 class 사용시 기본 상속 방식은 private, struct사용시 기본 상속 방식은 이지만 public표시 하는 것이 가장 좋습니다.

상속을 쓰다

5. 실제로 상속은 일반적으로 사용되며 상속은 publicb거의 사용되지 않으며 사용을 옹호하지 않습니다.protetced/private

protetced/private상속, 상속된 멤버는 파생 클래스의 클래스에서만 사용할 수 있기 때문에 protetced/private실제로는

연장 유지 보수는 강력하지 않습니다.

2. 기본 클래스 및 파생 클래스 개체의 복사 변환

  • 파생 클래스의 개체는 기본 클래스의 개체/기본 클래스의 포인터/기본 클래스의 참조 에 할당될 수 있습니다 . 썰다, 베다라는 아주 생생한 말이 있습니다. 파생 클래스에서 부모 클래스의 값을 잘라내어 할당하는 것입니다.

  • 기본 클래스의 개체를 파생 클래스에 할당할 수 없습니다.

  • 캐스팅을 통해 기본 클래스에 대한 포인터를 파생 클래스에 대한 포인터에 할당할 수 있습니다. 그러나 기본 클래스의 포인터가 파생 클래스 객체를 가리키는 경우에만

    는 안전하다. RTTI여기서 기본 클래스가 다형성 유형인 경우 (런타임 유형 정보)의 dynamic_cast를 사용하여

    식별이 수행된 후 보안 전환이 수행됩니다.

class Person
{
protected:
	string _name; // 姓名
	string _sex; // 性别
	int _age; // 年龄
};
class Student : public Person
{
public:
	int _No; // 学号
};
void Test()
{
	Student sobj;
	// 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj;//Person = Student
	Person* pp = &sobj;//Person = Student
	Person& rp = sobj;//Person = Student

	//2.基类对象不能赋值给派生类对象
	sobj = pobj;//Student = Person

	// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
	pp = &sobj;
	Student * ps1 = (Student*)pp; // 这种情况转换时可以的。Student* = (Student*)Person*
	ps1->_No = 10;

	pp = &pobj;//Person =Person
	Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
	ps2->_No = 10;
}

3. 상속 범위

1. 상속 시스템에서 기본 클래스파생 클래스는 독립적인 범위를 가집니다.

2. 서브클래스와 부모클래스에 이름이 같은 멤버가 존재하는데, 서브클래스 멤버와 부모클래스 멤버의 관계를 은닉이라고 합니다. 액세스를 표시하려면

3. 함수는 숨겨진 함수를 형성하기 위해 동일한 함수 이름만 가질 필요가 있으며 매개변수는 관련이 없습니다.

4. 같은 이름으로 멤버를 정의하지 않는 것이 가장 좋습니다.

class Person
{
public:

protected:
	string _name="牡丹";
	int _num=18;
};

class Student :public Person
{
public:
	void Print()
	{
		cout << "姓名:" << _name << endl;
		cout << "学号:" << _num << endl;
		cout << "学号:" << Person::_num << endl;
	}
protected:
	int _num = 520;
};

int main(void)
{
	Student s1;
	s1.Print();
	return 0;
}

작업 결과:

class Person
{
public:
	void Print(int a)
	{
		cout << "姓名:" << _name << endl;
		cout << "学号:" << _num << endl;
	}
protected:
	string _name="牡丹";
	int _num=18;
};

class Student :public Person
{
public:
	void Print()
	{
		Person::Print(1);
		cout << "姓名:" << _name << endl;
		cout << "学号:" << _num << endl;
		cout << "学号:" << Person::_num << endl;
	}
protected:
	int _num = 520;
};

int main(void)
{
	Student s1;
	s1.Print();
	return 0;
}

여기서 구성하는 것은 동일한 범위에 있지 않기 때문에 오버로드가 아니라 기능을 숨기는 것입니다.

4. 파생 클래스의 기본 멤버 함수

6개의 기본 멤버 함수, 기본값은 우리가 작성하지 않는다는 것을 의미하며, 컴파일러는 자동으로 하나를 생성하도록 변경한 다음 파생 클래스에서 이러한

멤버 함수는 어떻게 생성됩니까?

1. 파생 클래스의 생성자는 기본 클래스의 생성자를 호출하여 기본 클래스의 일부를 초기화해야 하며, 기본 클래스에 기본 생성자가 없으면 파생 클래스의 초기화 목록 단계에서 명시적으로 호출해야 합니다. 건설자.

2. 파생 클래스의 복사 생성은 기본 클래스의 복사 생성을 호출하여 기본 클래스의 초기화를 완료해야 합니다.

3. 파생 클래스는 기본 클래스의 복사를 완료하기 위해 operator=기본 클래스를 호출해야 합니다 .operator=

4. 파생 클래스의 소멸자는 기본 클래스의 소멸자를 자동으로 호출하여 호출된 후 기본 클래스의 멤버를 정리합니다 . 이렇게 하면 파생 클래스가

개체가 먼저 파생 클래스 멤버를 정리한 다음 기본 클래스 멤버를 정리하는 순서입니다.

5. 파생 클래스 개체 초기화는 기본 클래스 생성을 먼저 호출한 다음 파생 클래스 생성을 호출합니다.

6. 파생 클래스 개체 소멸자 정리는 먼저 파생 클래스 소멸자를 호출한 다음 기본 클래스 소멸자를 조정합니다.

class Person
{
public:
	Person(const char* name = "牡丹")
		:_name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		:_name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		cout << "Person& operator=(const Person& p)" << endl;
		if (this != &p)
		{
			_name = p._name;
		}
		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}

protected:
	string _name;
};

class Student : public Person
{
public:
	Student(const char* name, int num)
		: Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}

	Student(const Student& s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}

	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}

	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
void Test()
{
	Student s1("jack", 18);
	Student s2(s1);
	Student s3("rose", 17);
	s1 = s3;
}

int main(void)
{
	Test();
	return 0;
}

이 코드:

实例化对象s1的时候会先去调用Student的构造函数,然后再Student的构造函数当中显示的调用了父类的构造函数。
实例化对象s2的时候先调用Student的拷贝构造,在拷贝构造的初始化列表当中显示的调用父类的拷贝构造
实例化s3的时候和s1一样
s1赋值给s3的时候先去调用子类的operator=,但是在子类当中会调用父类的operator=

상속과 친구

friend 관계는 상속될 수 없습니다 . 즉, 기본 클래스 friend는 하위 클래스의 private 및 protected 멤버에 액세스할 수 없습니다.

Display는 베이스 클래스인 Person의 친구입니다.Student도 Person에서 우정 관계를 상속받는다면 여기서 s._stuNum은 접근 가능하지만 결과는 접근 불가능하므로 우정 관계를 계승할 수 없습니다.

6. 상속 및 정적 멤버

**기본 클래스가 정적 정적 멤버를 정의하는 경우 전체 상속 시스템에 해당 멤버가 하나만 있습니다. ** 서브클래스를 아무리 많이 파생시켜도 하나밖에 없다.

정적 멤버 인스턴스

class Person
{
public :
 Person () {++ _count ;}
protected :
 string _name ; // 姓名
public :
 static int _count; // 统计人的个数。
};
int Person :: _count = 0;
class Student : public Person
{
protected :
 int _stuNum ; // 学号
};
class Graduate : public Student
{
protected :
 string _seminarCourse ; // 研究科目
};
void TestPerson()
{
 Student s1 ;
 Student s2 ;
 Student s3 ;
 Graduate s4 ;
 cout <<" 人数 :"<< Person ::_count << endl;
 Student ::_count = 0;
 cout <<" 人数 :"<< Person ::_count << endl;
}

복잡한 다이아몬드 상속 및 다이아몬드 가상 상속

단일 상속: 하위 클래스에 직계 부모 클래스가 하나만 있는 경우 상속 관계를 단일 상속이라고 합니다.

다중 상속: 하위 클래스에 직계 상위 클래스가 둘 이상 있는 경우 상속 관계를 다중 상속이라고 합니다.

다이아몬드 상속: 다이아몬드 상속은 다중 상속의 특별한 경우입니다.

다이아몬드 상속 문제: 다음 개체 구성원 모델 구성에서 다이아몬드 상속에는 데이터 중복 및 모호성 문제가 있음을 알 수 있습니다. Assistant 개체에는 Person 멤버의 복사본이 두 개 있습니다.

class Person
{
public:
	string _name; // 姓名
};

class Student : public Person
{
protected:
	int _num; //学号
};

class Teacher : public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";

	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

가상 상속은 다이아몬드 상속의 모호성과 데이터 중복 문제를 해결할 수 있습니다.


class Person
{
public:
	string _name; // 姓名
};

class Student :virtual public Person
{
protected:
	int _num; //学号
};

class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};

class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};

void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";

	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

int main(void)
{
	Test();
	return 0;
}

이런 식으로 그들은 모두 _name같은 것을 가리킨다 _name.

7. 데이터 중복 및 모호성을 해결하기 위한 가상 상속의 원리

가상 상속의 구성원은 모두 공용 영역에 배치되는 것을 볼 수 있지만 여기에는 두 개의 포인터처럼 보이는 두 개의 주소가 더 있습니다.

14여기에 합이 있다는 것을 알 수 있습니다 0c. 14의 16진수는 20이고 c는 12이며 현재 클래스에서 공용 영역까지의 거리를 기록할 뿐입니다.

추천

출처blog.csdn.net/AkieMo/article/details/131625705