【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解

这篇具有很好参考价值的文章主要介绍了【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。
🎯每日格言:每日努力一点点,技术变化看得见。


继承的概念及定义

继承的概念

我们生活中也有继承的例子,例如:小明继承了孙老师傅做拉面的手艺。继承就是一种延续、复用的方式。C++为了提高代码的可复用性,引入了继承机制,概念如下↓↓↓

继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用

继承的定义

定义格式

下图演示的继承的格式,其中Person是父类,也称作基类;Student是子类,也称作派生类
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

下面给出代码示例(下面代码中,Student类继承父类Person)↓↓↓

#include <iostream>
using namespace std;

class Person
{
public:
	void Show()
	{
		cout <<  _name << " " << _age << endl;
	}
protected:
	string _name = "jammingpro";	//姓名
	int _age = 18;					//年龄
};

class Student : public Person
{
private:
	int _stuId;
};

int main()
{
	Person p;
	Student s;
	p.Show();
	s.Show();
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
上面代码中,Student继承父类Person的成员(成员函数+成员变量)后,这些成员都变成了子类的一部分。这里的Student复用了Person的成员。通过监视窗口可以看到,Student中也有自己的_name、_age成员变量。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

继承关系与访问限定符

在C++的继承机制中,包含3种继承方式及3种类访问限定符(如下图所示),下面将分别介绍它们。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

我们在学习类和对象时,就已经接触过访问限定符。其中public成员可以在类外访问,而protected与private成员不能在类外访问。但这里的protected和private在继承时是有区别的:
●如果父类愿意让自己的成员被外界访问并愿意让子类继承,则定义为public的;
●如果父类希望自己的成员不被外界访问而愿意让子类继承,则需要定义为protected;
●如果父类不希望自己的成员被外界访问、被继承,则需要定义为private的。

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

父类中的访问限定符表示父类愿不愿意让子类继承,而继承方式则可以让子类缩小父类成员的访问权限,但不能放大父类成员的访问权限。

父类成员/子类继承方式 public继承 protected继承 private继承
父类的public成员 变为子类的public成员 变为子类protected成员 变为子类的private成员
父类的protected成员 变为子类的protected成员 变为子类的protected成员 变为子类的private成员
父类的private成员 子类不可见 子类不可见 子类不可见

总结

  1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面
    都不能去访问它。

  2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

  3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式等于Min{成员在基类的访问限定符,继承方式},其中,public>protected>private。

  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。

  5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

针对于总结中的第一点,父类private成员实际上还是被子类继承了,只是子类无法访问,下面使用代码验证↓↓↓

#include <iostream>
using namespace std;

class Base
{
private:
	int _base;
};

class Son : public Base
{}

int main()
{
	Son s;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

基类和派生类对象赋值转换

●派生类对象可以赋值给基类的对象/基类的指针/基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
下面给出代码示例↓↓↓

#include <iostream>
using namespace std;

class Person
{
public:
	Person()
	{}
	Person(const string& name, const char& sex, const int& age)
		:_name(name)
		,_sex(sex)
		,_age(age)
	{}
protected:
	string _name;
	char _sex;
	int _age;
};

class Student : public Person
{
public:
	Student(const string& name, const char& sex, const int& age, const int& stuId)
		:Person(name, sex, age)
		,_stuId(stuId)
	{}
private:
	int _stuId;
};

int main()
{
	Student s("Jammingpro", 'M', 18, 123456);
	Person p;
	p=s;
	return 0;
}

从监视窗口可以看到,Person对象保存了Student对象的父类成员部分,而舍弃了子类自有成员,这就是切片。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

●基类对象不能赋值给派生类对象。(基类对象无法用于构造派生类对象,也无法使用派生类对象的拷贝赋值函数;但可以显示提供派生类赋值给基类的operator=实现)

★ps:由于派生类中的成员函数、成员对象一般情况下都会多于基类,如果基类直接赋值给派生类会导致部分成员数值不确定。因此,C++默认不提供基类赋值给派生类。

●基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针指向派生类对象时才是安全的。

下面代码演示了基类赋值给派生类指针,派生类赋值给基类指针↓↓↓

#include <iostream>
using namespace std;

class Person
{
public:
	Person()
	{}
	Person(const string& name, const char& sex, const int& age)
		:_name(name)
		, _sex(sex)
		, _age(age)
	{}
	string _name;
	char _sex;
	int _age;
};

class Student : public Person
{
public:
	Student(const string& name, const char& sex, const int& age, const int& stuId)
		:Person(name, sex, age)
		, _stuId(stuId)
	{}
	int _stuId;
};

int main()
{
	Student s("Jammingpro", 'M', 18, 123456);
	Person p("xiaoming", 'M', 20);
	
	Person* p_s = &s;//安全
	Student* s_p = (Student*) & p;//不安全

	cout << s_p->_stuId << endl;

	return 0;
}

为什么说,Student* s_p = (Student*) & p;是不安全的呢?由于Person对象中没有申请_stuId的空间,但在Student*类型看来,它认为它指向的对象有_stuId成员。如果用户访问了s_p->_stuId可能会因为内存非法访问而报错。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏(而不需要返回值相同,或是参数列表相同)。
  4. 注意在实际中在继承体系里面最好不要定义同名的成员。

下面给出派生类成员变量与基类成员变量同名的例子↓↓↓
下面代码中,由于Son类中的成员变量与Base类中的成员变量重名,构成了隐藏。如果使用Son s; cout << s._name << endl;,则只能访问到Son对象中的成员变量,而无法访问到父类中的_name成员变量。若需要访问父类的_name成员变量,则可以使用类型+类作用域符号::来访问,即s.Base::_name

#include <iostream>
using namespace std;

class Base
{
public:
	string _name = "Jammingpro";
};

class Son : public Base
{
public:
	string _name = "xiaoming";
};

int main()
{
	Son s;
	cout << s._name << endl;
	cout << s.Base::_name << endl;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
下面给出派生类成员函数与基类成员函数同名的例子↓↓↓这里与成员变量同名的情况相同,同名成员函数也会构成隐藏关系,如果需要访问父类的同名成员函数,需要使用类名+类作用域运算符::

#include <iostream>
using namespace std;

class Base
{
public:
	void Show()
	{
		cout << "I am _Base" << endl;
	}
};

class Son : public Base
{
public:
	void Show()
	{
		cout << "I am _Son" << endl;
	}
};

int main()
{
	Son s;
	s.Show();
	s.Base::Show();
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
★( ఠൠఠ )ノtest:下面的两个同名函数(函数名相同,参数列表不同)分别属于基类和派生类,它们构成的关系是隐藏还是函数重载呢?

#include <iostream>
using namespace std;

class Base
{
public:
	void print(char ch)
	{
		cout << "Base->" << ch << endl;
	}
};

class Son : public Base
{
public:
	void print(int num)
	{
		cout << "Son->" << num << endl;
	}
};

int main()
{
	Son s;
	s.print('A');
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
Base中的成员函数比Son中的成员函数更匹配(不需要隐式类型转换),而这里还是调用Son中的成员函数,说明两者构成的关系是隐藏,而不是函数重载。这里要注意:在继承关系中,派生类与基类只要存在同名函数(不管参数列表、返回值是否相同),都是隐藏关系。

派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个成员函数是如何生成的呢?

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

基类提供默认构造函数的情况
派生类在构造时,会自动调用基类的构造函数↓↓↓

#include <iostream>
using namespace std;

class Base
{
public:
	Base()
	{
		cout << "Base() is called" << endl;
	}
};

class Son : public Base
{
public:
	Son()
	{
		cout << "Son() is called" << endl;
	}
};

int main()
{
	Son s;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
基类没有提供默认构造函数的情况
基类没有提供默认构造时,子类必须在初始化参数列表中显示调用基类的构造函数,否则会报错。

#include <iostream>
using namespace std;

class Base
{
public:
	Base(int b)
		:_b(b)
	{
		cout << "Base(int b) is called" << endl;
	}
private:
	int _b
};

class Son : public Base
{
public:
	Son(int b)
		:Base(b)
	{
		cout << "Son() is called" << endl;
	}
};

int main()
{
	Son s(5);
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

  1. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
#include <iostream>
using namespace std;

class Base
{
public:
	Base(int b)
		:_b(b)
	{}
	Base(const Base& b)
	{
		_b = b._b;
		cout << "Base(const Base& b) is called" << endl;
	}
private:
	int _b;
};

class Son : public Base
{
public:
	Son(int s, int b)
		:_s(s)
		,Base(b)
	{}
	Son(const Son& s)
		:Base(s)
	{
		_s = s._s;
		cout << "Son(const Son& s) is called" << endl;
	}
private:
	int _s;
};

int main()
{
	Son s(55, 66);
	Son s2(s);
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
如果我们将上面代码中显示调用基类构造函数的代码去掉,则会出现如下报错↓↓↓
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
★ps:对于上面的报错,虽然可以通过给基类提供默认构造函数解决,但却无法完成子类中的基类成员的拷贝操作。

  1. 派生类的operator=可以调用基类的operator=完成基类的复制,这样可以避免代码冗余(这里派生类调用基类的operator=不是必须的,只是因为基类已经实现了该操作,派生类不必再重复编写相同内容)。

下面代码演示了派生类调用基类operator=函数带来的代码简化↓↓↓

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

class Person
{
public:
	Person(const string& name, const int& age, const char& gender)
		:_name(name)
		,_age(age)
		,_gender(gender)
	{}
	Person& operator=(const Person& p)
	{
		_name = p._name;
		_age = p._age;
		_gender = p._gender;
		return *this;
	}
	void Show()
	{
		cout << "My name is " << _name << ", I am " << _age << " years old, I am a " 
		<< (_gender == 'M' ? " boy " : "girl") << endl;
	}
private:
	string _name;
	int _age;
	char _gender;
};

class Student : public Person
{
public:
	Student(const string& name, const int& age, const char& gender, const char* detail)
		:Person(name, age, gender)
	{
		_detail = new char[strlen(detail) + 1];
		strcpy(_detail, detail);
	}
	Student& operator=(const Student& s)
	{
		Person::operator=(s);
		char* detail = new char[strlen(s._detail)];
		return *this;
	}
	void Show()
	{
		Person::Show();
		cout << "My detail infomation is " << _detail << endl;
	}
	~Student()
	{
		delete[] _detail;
	}
private:
	char* _detail;
};

int main()
{
	Student s("Jammingpro", 18, 'M', "He is good at coding");
	Student copy = s;
	s.Show();
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

  1. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

  2. 派生类对象初始化先调用基类构造再调派生类构造。

下面代码演示了基类与派生类的构造与析构顺序↓↓↓

#include <iostream>
using namespace std;

class Base
{
public:
	Base()
	{
		cout << "Base() is called" << endl;
	}
	~Base()
	{
		cout << "~Base() is deleted obj" << endl;
	}
};

class Son : public Base
{
public:
	Son()
	{
		cout << "Son is called" << endl;
	}
	~Son()
	{
		cout << "~Son() is deleted obj" << endl;
	}
};

int main()
{
	Son s;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

  1. 编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

★ps:关于virtual关键字将于多态中讲解

继承与友元

友元关系不能不继承,也就是说:基类的友元不能访问派生类的私有和保护成员。

下面代码演示了友元关系无法继承↓↓↓

#include <iostream>
using namespace std;

class Base
{
	friend void print();
private:
	int _base = 88;
};

class Son : Base
{
private:
	int _son = 66;
};

void print()
{
	Base b;
	cout << b._base << endl;
	Son s;
	cout << s._son << endl;
}

int main()
{
	print();
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

继承与静态成员

基类定义了static静态成员,则整个继承体系中只能有一个这样的成员。无论派生出多少多少个子类,都只有一个static成员实例。

#include <iostream>
using namespace std;

class Base
{
public:
	static int val;
};

//静态非const成员变量需要在类外初始化
int Base::val = 66;

class Son : public Base
{
};

class GrandSon : Son
{
};

int main()
{
	cout << &Base::val << endl;
	cout << &Son::val << endl;
	cout << &GrandSon::val << endl;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
下面是一份单继承的代码↓↓↓

#include <iostream>
using namespace std;

class Base
{
public:
	void base_func()
	{}
	int _base;
};

class Son : public Base
{
public:
	void son_func()
	{}
	int _son;
};

class GrandSon : public Son
{
public:
	void gs_func()
	{}
	int _gs;
};

int main()
{
	GrandSon gs;
	cout << &gs << endl;
	gs._base = 1;
	cout << &gs._base << endl;
	gs._son = 2;
	cout << &gs._son << endl;
	gs._gs = 3;
	cout << &gs._gs << endl;

	cout << "===================================" << endl;

	Son s;
	cout << &s << endl;
	s._base = 1;
	cout << &s._base << endl;
	s._son = 2;
	cout << &s._son << endl;

	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

上述代码调试时,通过监视窗口查看结果如下图所示。我们可以发现,GrandSon对象保存了其祖先类Son、Base的成员变量。Son对象保存了其基类成员变量。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
下图为GrandSon对象的存储情况↓↓↓
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
通过分析上面的执行结果,可以得出如下结论:再单继承中,某个类的成员变量放置于类空间最后,该成员变量前放置的是直接父类,再往上是爷爷类,以此类推。类对象的地址,与最顶层的祖先的成员变量地址相同。

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
下面给出多继承的演示代码↓↓↓

#include <iostream>
using namespace std;

class Base1
{
public:
	void base1_func()
	{}
	int _base1;
};

class Base2
{
public:
	void base2_func()
	{}
	int _base2;
};

class Son : public Base1, public Base2
{
public:
	void son_func()
	{}
	int _son;
};

int main()
{
	Son s;
	cout << &s << endl;
	s._base1 = 1;
	cout << &s._base1 << endl;
	s._base2 = 2;
	cout << &s._base2 << endl;
	s._son = 3;
	cout << &s._son << endl;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
如果Son先继承Base1再继承Base2,则会将Base1的成员变量放在前面,后继承的Base2的成员变量放在后面。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
如果将Son先继承Base2,再继承Base1呢?
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

由上面的执行结果可知,先继承的基类的成员变量放置于类对象的前面位置,后即成的基类的成员变量放置于类对象的后面位置,类自身的成员变量放置于最后。

菱形继承:菱形继承是多继承的一种特殊情况
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
下面给出多继承的演示代码↓↓↓

#include <iostream>
using namespace std;

class Share
{
public:
	void share_func()
	{}
	int _share;
};

class Base1 : Share
{
public:
	void base1_func()
	{}
	int _base1;
};

class Base2 : Share
{
public:
	void base2_func()
	{}
	int _base2;
};

class Son : public Base1, public Base2
{
public:
	void son_func()
	{}
	int _son;
};

int main()
{
	Son s;
	s._base1 = 1;
	s._base2 = 2;
	s._son = 3;
	return 0;
}

在上述代码的监视窗口可以看出菱形继承有数据冗余和二义性的问题。s中继承了两份Share类的成员变量_share。
【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言
C++中为了避免菱形继承导致的数据冗余和二义性,它引入了虚拟继承。虚拟继承可以解决菱形继承的二义性和数据冗余的问题。下面给出修改后的代码(引入虚拟继承的代码)↓↓↓

#include <iostream>
using namespace std;

class Share
{
public:
	void share_func()
	{}
	int _share;
};

class Base1 : virtual public Share
{
public:
	void base1_func()
	{}
	int _base1;
};

class Base2 : virtual public Share
{
public:
	void base2_func()
	{}
	int _base2;
};

class Son : public Base1, public Base2
{
public:
	void son_func()
	{}
	int _son;
};

int main()
{
	Son s;
	cout << &s << endl;
	s._share = 0;
	cout << &s._share << endl;
	s._base1 = 1;
	cout << &s._base1 << endl;
	s._base2 = 2;
	cout << &s._base2 << endl;
	s._son = 3;
	cout << &s._son << endl;
	return 0;
}

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解,浅尝C++,c++,开发语言,c语言

这里是通过了两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的Share。

🎈欢迎进入浅尝C++专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d文章来源地址https://www.toymoban.com/news/detail-846131.html

到了这里,关于【浅尝C++】继承机制=>虚基表/菱形虚继承/继承的概念、定义/基类与派生类对象赋值转换/派生类的默认成员函数等详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】从0到1讲继承|复杂的菱形继承

        个人主页:🍝在肯德基吃麻辣烫 我的gitee:gitee仓库 分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处。 本文主要讲述的是继承的概念,以及基类和派生类和衍生出的各种东西,还有多继承,菱形继承等,从0到1讲解继承。 与日常生活中的人的继承相关,你可

    2024年02月16日
    浏览(27)
  • 【C++练级之路】【Lv.12】继承(你真的了解菱形虚拟继承吗?)

    快乐的流畅:个人主页 个人专栏:《C语言》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 继承(inheritance),是面向对象的三大特性之一。 它是面向对象编程中, 使代码可以复用 的最重要的手段,它允许程序员在 保持原有类特性的基础上进行扩展

    2024年03月14日
    浏览(35)
  • C++中菱形继承中的多态在底层是如何实现的。

    如果还不了解菱形继承和多态的底层可以看这两篇文章: C++中多态的底层实现_Qianxueban的博客-CSDN博客 C++的继承以及virtual的底层实现_Qianxueban的博客-CSDN博客

    2024年02月09日
    浏览(27)
  • 【继承】复杂的菱形继承

    博主首页:  有趣的中国人   专栏首页:  C++进阶   本篇文章主要讲解 菱形继承   的相关内容 目录 1. 继承与友元 2. 继承与静态成员 3. 复杂的菱形继承及菱形虚拟继承 3.1 继承分类 3.2 菱形继承导致的问题 3.3 虚拟继承解决数据冗余的原理 4. 继承和组合的区别 友元关系不

    2024年04月22日
    浏览(23)
  • C++--菱形继承

    1.什么是菱形继承         单继承:一个子类只有一个直接父类时称这个继承关系为单继承                                            多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承 菱形继承的问题:菱形继承有数据冗余和二义性

    2024年02月15日
    浏览(27)
  • 【C++技能树】继承概念与解析

    Halo,这里是Ppeua。平时主要更新C++,数据结构算法,Linux与ROS…感兴趣就关注我bua! ​ 设想一个场景,你需要设计学生、老师、教授…的类,除了每个身份中独有的信息,例如:学号,工号,教授身份号,但是他们都有一个共同的属性,就是人.所以我们可以先设计一个类:人. 每设计一个

    2024年02月10日
    浏览(28)
  • C++虚基类

    如果一个派生类是从多个基类派生出来的,而这些基类又有一个共同的基类,则在这个派生类中访问这个共同的基类中的成员时,可能会产生二义性。 比如有以下结构 以下程序会报错,因为a具有二义性。 能够看到a被初始化了两次,我们希望a只被再第一次初始化以后就不再

    2024年02月08日
    浏览(33)
  • 想要入坑C++?当我拿出菱形虚拟继承,阁下又该如何应对

    🌸作者简介: 花想云 ,目前大二在读 ,C/C++领域新星创作者、运维领域新星创作者、CSDN2023新星计划导师、CSDN内容合伙人、阿里云专家博主、华为云云享专家致力于 C/C++、Linux 学习 🌸 本文收录于 C++系列 ,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造

    2024年02月07日
    浏览(26)
  • 【C++】继承的基本特性(定义,赋值转换,友元,静态成员,虚拟继承,默认成员函数,作用域)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。 Person是父类,也称作基类。Student是子类,也称作派生类 总结: 基类private成员

    2024年02月14日
    浏览(32)
  • 论文浅尝 | 记忆力强还是健忘?深入探究语言模型的知识记忆机制

    笔记整理:方润楠,浙江大学硕士,研究方向为自然语言处理 链接:https://arxiv.org/abs/2305.09144 摘要 近年来,大规模预训练语言模型展示出惊人的记忆能力,相比之下,未经预训练的普通神经网络存在着灾难性遗忘的问题。为了研究这种记忆与遗忘出现的原因,并探求语言模

    2024年01月18日
    浏览(26)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包