首页 > 编程语言 >c++中的继承和多态

c++中的继承和多态

时间:2024-09-14 22:20:53浏览次数:15  
标签:继承 基类 多态 c++ public Person 派生类 class

目录

 

Linux中的管道通信

​编辑派生类的默认成员函数

继承 

 派生类的构造

 隐藏

如何设计一个不能被继承的类

菱形继承

virtual

virtual是如何解决的

内存对象模型

继承和组合

继承

组合

多态

概念

多态的构成条件

虚函数的重写

Linux中的管道通信

派生类的默认成员函数

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

2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

3. 派生类的operator=必须要调用基类的operator=完成基类的复制。

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

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

6. 派生类对象析构清理先调用派生类析构再调基类的析构。

继承 

class Person
{
public:
	Person(const char* name = "peter")
		: _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
{
	Student(const char* name, int stuid)
		:Person(name)
		,_stuid(stuid)
	{}
private:
	int _stuid;
};

 Person(name)  相当于构造了一个匿名对象。

派生类的构造  其中有一部分继承自父类   只能自动调默认的构造函数(编译器默认生成的,全缺省的,无参的)

class Student : public Person
{
public:
	Student(const char* name, int stuid)
		:Person(name)
		,_stuid(stuid)
	{
		cout << "Student()" << endl;
	}
	Student(const Student& s)
		:Person(s)
		, _stuid(s._stuid)
	{
		cout << "Student(const Student& s)" << endl;
	}
private:
	int _stuid;
};

 隐藏

子类和父类有同名函数会构成隐藏

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

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

子类隐藏了父类的operator=会引发堆栈溢出。

更正:

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

子类和父类的析构函数同样构成隐藏。因为其名字会被编译器替换为destructor.

~Student()
{
	//Person::~Person();
	cout << "~Student()" << endl;
}

结束时会自动调用父类的析构函数,因为这样才能保证先析构子类在析构父类

如何设计一个不能被继承的类

class A
{
private:
	A()
	{}
};
class B : public A
{};

构造函数私有化后,B类无法实例化出对象。

子类在调用构造函数时必须先调用父类的。

友元关系不能被继承。

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

菱形继承

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";
}

virtual

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承 Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。

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

virtual是如何解决的

c++的缺陷有那些?

多继承->菱形继承->虚继承->底层结构的对象模型非常复杂,且有一定的效率损失。

什么事菱形继承?菱形继承存在的问题?如何解决?-> 虚继承  虚继承的解决原理是什么?

内存对象模型

对象在内存中是如何存的?

class A
{
public:
	int _a;
};

// class B : public A
class B : virtual public A
{
public:
	int _b;
};

// class C : public A
class C : virtual public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};

int main()
{
	D d;
	cout << sizeof(d) << endl;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	d._a = 6;
	return 0;
}

继承和组合

都可以完成类层次的复用

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。

组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

继承

继承是一种白箱复用,父类对子类基本是透明的,但在一定程度上破坏了父类的封装性。

class A{};
class B : class A
{};

例如:车和车品牌之间的关系 

组合

组合是一种黑箱复用,c对d是不透明的,很好的保持了c的封装性。

class C {};
class D
{
    C c;
};

例如轮胎和车的关系。

多态

概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状态。

多态的构成条件

1. 必须通过基类的指针或者引用调用虚函数

2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类 型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
 
class Student : public Person {
public:
 virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后
基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用*/
 /*void BuyTicket() { cout << "买票-半价" << endl; }*/
};
 
void Func(Person& p)
{ p.BuyTicket(); }
 
int main()
{
 Person ps;
 Student st;
 
 Func(ps);
 Func(st);
 
 return 0;
}

标签:继承,基类,多态,c++,public,Person,派生类,class
From: https://blog.csdn.net/2301_77479435/article/details/142250098

相关文章

  • C++:初始化列表、友元、static
    目录一、初始化列表​二、static成员三、友元函数一、初始化列表•之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方 式,就是初始化列表,初始化列表的使用方式是以⼀个冒号开始,接着是⼀个以逗号分隔的数据成员列表,每个"成员变量"后......
  • 「数学::质数」埃氏筛|欧拉筛(埃拉托斯特尼筛法|线性筛法)/ LeetCode 204(C++)
    目录概述1.埃氏筛思路复杂度Code2.欧拉筛(线性筛)思路复杂度Code总结概述上一节我们介绍了对判断一个数是否为质数的方法:「数学::质数」试除法/LuoguP5736(C++)那如果我们期望输出一个范围内的所有质数,使用试除法的时间复杂度是n√n,怎么办呢?LeetCode204:给定整......
  • 【C++基础概念理解——类的继承和嵌套】
    基本概念在C++中,类的继承和嵌套类的定义是两种不同的概念。classInitialSetupProcedure:publicStateMachine//类的继承(符号是":")classInitialSetupProcedure::StateMachine//类的嵌套(符号是"::",意思是类StateMachine嵌套在InitialSetupProcedure类中)......
  • stm32驱动HX711称重传感器 c++代码分享
    一、HX711模块介绍HX711模块是一种专门用于称重传感器的放大器模块。它的主要功能是将测得的微小电压信号放大到可以被微控制器读取的范围。HX711模块通常配合称重传感器一起使用,例如压力传感器、负载细胞等。它采用24位的模数转换器(ADC)来精确测量传感器的电压变化。HX711模块具......
  • C++ 顶层const底层const
    inti=0;int*constpl=&i;//不能改变p1的值,这是一个顶层constconstintci=42;//不能改变ci的值,这是一个顶层constconstint*p2=&ci;//允许改变p2的值,这是一个底层constconstint*constp3=p2;//靠右的const是顶层const,靠左的是底层constconstin......
  • c++临时对象导致的生命周期问题
    对象的生命周期是c++中非常重要的概念,它直接决定了你的程序是否正确以及是否存在安全问题。今天要说的临时变量导致的生命周期问题是非常常见的,很多时候没有一定经验甚至没法识别出来。光是我自己写、review、回答别人的问题就犯了或者看到了许许多多这类问题,所以我想有必要......
  • c++类和对象(3):默认成员函数(下)
    1.拷贝构造函数如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。c++规定:类类型的传值传参必须用拷贝构造1.1拷贝构造函数的特点1.拷贝构造函数是构造函数的⼀个重载......
  • 南沙C++noip老师解一本通题: 1360:奇怪的电梯(lift)
    ​【题目描述】大楼的每一层楼都可以停电梯,而且第i层楼(1≤i≤N)上有一个数字Ki(0≤=Ki≤=N)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如:33125代表了Ki(K1=3,K2=3,……),从一楼开始。在一楼,按“上”可以到4......
  • 【关于c++模版类的报错问题】
    关于c++模版类的报错问题模版类的定义使用类模板模版类的定义通常在实现一个类时,会在.h头文件中声明函数,在.cpp文件中实现该函数。然而如果是模版类的话情况则会稍有不同。这是一个名为debug.h的头文件,里面包含了一个名为A的模版类类A的实现在debug.hpp中,模板类的......
  • C#实现多态的几种
    C#实现多态的几种,转载地址https://www.cnblogs.com/Jerry9521/p/16246870.html一:多态的理解父类类型和子类类型可以使用同一个(方法名的)方法而输出不同的结果;例如父类可以使用虚函数,子类可以选择重写虚函数(或者不重写),子类对象调用方法的时候可以选择使用父类中的虚方法或者子类......