C++学习路线(二十六)

继承与派生

继承

除了"构造函数"和"析构函数"父类的所有成员函数,以及数据成员,都会被子类继承.

Father.h

#pragma once
#include <string>
using namespace std;
class Father {
public:
	Father(const char* name, int age);
	~Father();
	string getName() const;
	int getAge() const;
	string description() const;
private:
	int age;
	string name;
};

Father.cpp

cpp 复制代码
#include "Father.h"
#include "sstream"
Father::Father(const char* name , int age){
	this->name = name;
	this->age = age;
}
Father::~Father() {

}
int Father::getAge() const {
	return age;
}

string Father::getName() const {
	return name;
}

string Father::description() const {
	stringstream ss;
	ss << "Name: " << name << ", Age: " << age;
	return ss.str();
}

Son.h

cpp 复制代码
#pragma once
#include "Father.h"
class Son : public Father {
public:
	Son(const char* name, int age, const char* game);
	~Son();
	string getGame() const;
	string description() const;
private:
	string game;
};

Son.cpp

cpp 复制代码
#include "Son.h"
#include <iostream>
#include <sstream>
Son::Son(const char* name, int age, const char* game) : Father(name , age) {
	this->game = game;
	std::cout << __FUNCTION__ << " called" << std::endl;
}	

Son::~Son() {

}

string Son::getGame() const {
	return game;
}
string Son::description() const {
	stringstream ss;
	ss << "Son: " << Father::description() << " plays " << game;
	return ss.str();
}

main.cpp

cpp 复制代码
#include <iostream>
#include "Father.h"
#include "Son.h"

int main() {
	Father f("John", 25);
	Son s("Tom", 18, "game");
	cout << f.description() << endl;
	cout << s.description();
}

子类,一般会添加自己的数据成员/成员函数,或者,重新定义从父类继承的方法!!!子类对象就会调用自己重新定义的方法,不会调用父类的同名方法

派生类对象的内存分布

我们怎么在vs看类对象的内存分布?

/d1 reportSingleClassLayoutFather /d1 reportSingleClassLayoutSon

cpp 复制代码
#include <iostream>
#include "Father.h"
#include "Son.h"

int main() {
	Father f("John", 25);
	Son s("Tom", 18, "game");
	cout << "string size: " << sizeof(f.name) << endl;
	cout << "father size: " << sizeof(f) << endl;
	cout << "son size: " << sizeof(s) << endl;
	return 0;
}

说明:成员函数,不占用对象的内存空间,但是也被子类继承了!

protected访问权限

为什么要使用 protected访问权限?

子类的成员函数中,不能直接访问父类的private 成员,已经这些成员已经被继承下来了但是却不能访问。

只有通过父类的public函数来间接访问,不是很方便比如,刚才 Demo中 Father类中的 name和age 成员。
解决方案:

把 name和 age 定义为 protected 访问访问权限
效果:

Son 类的成员函数中,可以直接访问它的父类的protected 成员但是在外部,别人又不能直接通过 Son对象来访问这些成员。

一个类,如果希望,它的成员,可以被自己的子类(派生类)直接访问,但是,又不想被外部访问那么就可以把这些成员,定义为 protected访问权限!!!

protected和private很相似 唯一的区别就是protected子类的成员可以直接访问。

派生和继承的各种方式

public公有继承

父类定义的成员函数和数据成员被子类继承之后,访问权限不变

当使用 public 关键词继承时,基类的 public 成员在派生类中依然是 public 的,而 protected 成员变为 protected 的,private 成员则不可访问

public->public

protected->protected

private->private

cpp 复制代码
#include <iostream>
using namespace std;

class MyClass {
public:
	void myPublicMethod() {
		cout << "MyClass::myPublicMethod()" << endl;
	}
	int publicInt = 10;
protected:
	void myProtectedMethod() {
		cout << "MyClass::myProtectedMethod()" << endl;
	}
	int protectedInt = 20;
private:
	void myPrivateMethod() {
		cout << "MyClass::myPrivateMethod()" << endl;
	}
	int privateInt = 30;
};

class MySubClass : public MyClass {
public:
	void MySubClassPublicMethod() {
		cout << "MySubClass::MySubClassPublicMethod()" << endl;
	}
protected:
	void MySubClassProtectedMethod() {
		cout << "MySubClass::MySubClassProtectedMethod()" << endl;
	}
private:
	void MySubClassPrivateMethod() {
		cout <<	"MySubClass::MySubClassPrivateMethod()" << endl;
	}
};

int main() {
	MySubClass mySubClass;
	return 0;
}

在类外访问是可以访问到子类的public和父类的public的

类内可以访问protected 和 public的数据成员和成员函数

private私有继承

父类中定义的成员(数据成员和函数成员)被继承后,访问权限都变成private

public->private

protected--> private

private -->private

父类定义的成员函数和数据成员被子类继承之后,访问权限全部变成private

当使用 private 关键词继承时,基类的所有成员(public, protectedprivate)在派生类中都变为 private并且只有派生类内部能够访问这些成员外部代码不能通过派生类的对象来访问基类的成员,即使是 public 成员也不行

cpp 复制代码
class MySubClass : private MyClass {
public:
	void MySubClassPublicMethod() {
		cout << "MySubClass::MySubClassPublicMethod()" << endl;
	}
protected:
	void MySubClassProtectedMethod() {
		cout << "MySubClass::MySubClassProtectedMethod()" << endl;
	}
private:
	void MySubClassPrivateMethod() {
		cout <<	"MySubClass::MySubClassPrivateMethod()" << endl;
	}
};

上面这个是类外访问数据成员 也就是只能访问public

下面这个是类内访问数据成员,类内也是能访问public 和 protected

所以我觉得 类继承的那个访问权限对类内没什么约束 该访问的还是可以访问(子类类内访问父类) 但是类外还是有区别

protected保护继承

public--> protected

protected -->protected

private--> private
小结:
public继承全不变
private 继承全变私
protected继承只把public降级为protected

但是 最重要的 这种继承 不是在直接子类 而是在子类的子类发挥作用

cpp 复制代码
#include <iostream>
using namespace std;

class MyClass {
public:
	void myPublicMethod() {
		cout << "MyClass::myPublicMethod()" << endl;
		privateInt = 10;
	}
	int publicInt = 10;
protected:
	void myProtectedMethod() {
		cout << "MyClass::myProtectedMethod()" << endl;
	}
	int protectedInt = 20;
private:
	void myPrivateMethod() {
		cout << "MyClass::myPrivateMethod()" << endl;
	}
	int privateInt = 30;
};

class MySubClass : private MyClass {
public:
	void MySubClassPublicMethod() {
		cout << "MySubClass::MySubClassPublicMethod()" << endl;
	}
protected:
	void MySubClassProtectedMethod() {
		cout << "MySubClass::MySubClassProtectedMethod()" << endl;
	}
private:
	void MySubClassPrivateMethod() {
		cout <<	"MySubClass::MySubClassPrivateMethod()" << endl;
	}
};

class MySubSubClass : public MySubClass {
public:
	void MySubSubClassPublicMethod() {
		cout << "MySubSubClass::MySubSubClassPublicMethod()" << endl;
		myPublicMethod();
	}
protected:
	void MySubSubClassProtectedMethod() {
		cout << "MySubSubClass::MySubSubClassProtectedMethod()" << endl;
	}
private:
	void MySubSubClassPrivateMethod() {
		cout << "MySubSubClass::MySubSubClassPrivateMethod()" << endl;
	}

}

int main() {
	MySubClass mySubClass;
	mySubClass.MySubClassPublicMethod();
	return 0;
}

我们看上面的代码 子类是private继承 所以子类的子类是不能访问 父类的

如果改成 protected或者public 是可以访问的

什么时候使用继承和派生

1)准备实现多个类,但是这些类在现实世界中有某种特殊关系(比如:类别与子类别的关系)

例如:人女人男人如果完全独立的实现这3个类,将有很多重复代码,而且不利于以后的维护。

2)准备构建一个类,但是这个类与己经开发好的某个类非常相似,而且在现实世界中具有某种特殊关系(比如:类别与子类别的关系)。

如果全部重新写这个新类,效率较低,因为有很多东西已经在这个已有的类中实现了实例

3)对多个已经实现的类(这些类有某种特殊关系),进行重构,一般在前两种情况使用,第3种(重构)是不得而为之。

子类的构造函数

显式调用父类的构造函数

cpp 复制代码
#include <iostream>
using namespace std;

class Father_ {
public:
	Father_() {
		cout << "Default constructor called" << endl;
	}
	Father_(int age, string name, char gender) {
		this->age = age;
		this->name = name;
		this->gender = gender;
	}
private:
	int age = 0;
	string name = "";
	char gender = 'M';
};

class Son_ : public Father_ {
public:
	Son_(int age, string name, char gender, int height) : Father_(age, name, gender), height(height) {
		cout << "Constructor called" << endl;
	}
	~Son_() {
		cout << "Destructor called" << endl;
	}
private:
	int height;
};
int main() {
	Son_ son(20, "John", 'M', 170);
	return 0;
}

如果没有显式调用的话会调用父类默认的构造函数

cpp 复制代码
#include <iostream>
using namespace std;

class Father_ {
public:
	Father_() {
		cout << "Default constructor called" << endl;
	}
	Father_(int age, string name, char gender) {
		this->age = age;
		this->name = name;
		this->gender = gender;
	}
private:
	int age = 0;
	string name = "";
	char gender = 'M';
};

class Son_ : public Father_ {
public:
	Son_(int age, string name, char gender, int height): height(height) {
		cout << "Constructor called" << endl;
	}
	~Son_() {
		cout << "Destructor called" << endl;
	}
private:
	int height;
};
int main() {
	Son_ son(20, "John", 'M', 170);
	return 0;
}

子类和父类的构造函数调用顺序

当创建子类对象时,构造函数的调用顺序:

静态数据成员的构造函数 ->父类的构造函数- > 非静态的数据成员的构造函数 ->自己的构造函数

注意:

无论创建几个对象,该类的静态成员只构建一次,所以静态成员的构造函数只调用1次!!!

cpp 复制代码
#include <iostream>
using namespace std;
class M {
public:
	M() {
		cout << __FUNCTION__ << endl;
	}
};
class N {
public:
	N() {
		cout << __FUNCTION__ << endl;
	}
};

class A {
public:
	A() {
		cout << __FUNCTION__ << endl;
	}
};

class B : public A {
public:
	B() {
		cout << __FUNCTION__ << endl;
	}
private:
	M m1;
	M m2;
	static N n1;
};

N B::n1;

int main() {
	B b;
	return 0;
}

可以按照上面的方法来推出来,就是 N先初始化,然后A初始化 ,然后M初始化,然后本身的B初始化。

子类的析构函数

子类的析构函数的调用顺序,和子类的构造函数的调用顺序相反

cpp 复制代码
#include <iostream>
using namespace std;
class M {
public:
	M() {
		cout << __FUNCTION__ << endl;
	}
	~M() {
		cout << __FUNCTION__ << endl;
	}
};
class N {
public:
	N() {
		cout << __FUNCTION__ << endl;
	}
	~N() {
		cout << __FUNCTION__ << endl;
	}
};

class A {
public:
	A() {
		cout << __FUNCTION__ << endl;
	}
	~A() {
		cout << __FUNCTION__ << endl;
	}
};

class B : public A {
public:
	B() {
		cout << __FUNCTION__ << endl;
	}
	~B() {
		cout << __FUNCTION__ << endl;
	}
private:
	M m1;
	M m2;
	static N n1;
};

N B::n1;

int main() {
	{
		B b;
	}
	cout << "main()" << endl;

	return 0;
}

细心的同学会发现 静态对象在程序终止时被销毁,所以:静态成员的析构函数,在程序结束前,是不会被调用的!

什么是子类型

cpp 复制代码
#include <iostream>
using namespace std;

class A {
public:
	A() {}
	~A() {}
	void kill() {
		cout << "A is dead" << endl;
	}
};
class B : public A {
public:
	B() {}
	~B() {}
	void kill() {
		cout << "B is dead" << endl;
	}
};

void test(A a) {
	a.kill();
}

int main() {
	A a;
	B b;
	test(a);
	test(b);
	return 0;
}

子类型的作用

在需要父类对象的任何地方,可以使用"公有派生"的子类的对象来替代,从而可以使用相同的函数统一处理基类对象和公有派生类对象即:形参为基类对象时,实参可以是派生类对象。

那么这里说共有派生 才可以 ,私有派生不行。

子类型的应用

1.基类(父类)的指针,可以指向这个类的公有派生类(子类型)对象。

Son yangGuo;

Father *f=&yangGuo:
2.公有派生类(子类型)的对象可以初始化基类的引用

Son yangGuo;

Father &f2= yangGuo.
3.公有派生类的对象可以赋值给基类的对象

Son yangGuo;

Father fl= yangGuo,

注意:以上的应用,反过来就会编译失败!

多重继承

什么是多重继承?

一个派生类可以有两个或多个基类(父类)。

多重继承在中小型项目中较少使用,在Java、C#等语言中直接取消多继承,以避免复杂性。

多重继承的用法

例如已经声明了类A,B,C,D 那么我们可以这样声明类D

cpp 复制代码
class D : public A , protected B , private C{

}
cpp 复制代码
#include <iostream>
using namespace std;

class Father {
public:
	Father(const char* lastName = "None", const char* firstName = "None") : lastName(lastName), firstName(firstName) {
		cout << "Father constructor called." << endl;
	}
	~Father() {}
	void football() {
		cout << "football." << endl;
	}
private:
	string lastName;
	string firstName;
};

class Mother {
public:
	Mother(const char* lastName = "None", const char* firstName = "None" , const char* food = "None") : lastName(lastName), firstName(firstName) , food("apple") {
		cout << "Mother constructor called." << endl;
	}
	~Mother() {}
	void dance() {
		cout << "dance." << endl;
	}
private:
	string lastName;
	string firstName;
	string food;
};

class Son : public Father, public Mother {
public:
	Son(const char* lastName = "None", const char* firstName = "None", const char* game = "None", const char* food = "None") : Father(lastName, firstName), Mother(lastName, firstName, food), game(game) {
	
	}
	~Son() {}
	void play() {
		cout << "Son play " << game << "." << endl;
	}
private:
	string game;
};

int main() {
	Son son("Liu", "Hong", "football", "apple");
	son.football();
	son.dance();
	son.play();
	return 0;
}

多继承的构造函数的调用顺序
基类构造函数的调用顺序和和它们在派生类构造函数中出现的顺序无关而是和声明派生类时基类出现的顺序相同

多重继承的弊端:二义性(钻石继承)

cpp 复制代码
#include <iostream>
using namespace std;

class Father {
public:
	Father(const char* lastName = "None", const char* firstName = "None") : lastName(lastName), firstName(firstName) {
		cout << "Father constructor called." << endl;
	}
	~Father() {}
	void football() {
		cout << "football." << endl;
	}
	void dance() {
		cout << "popping dance" << endl;
	}
private:
	string lastName;
	string firstName;
};

class Mother {
public:
	Mother(const char* lastName = "None", const char* firstName = "None" , const char* food = "None") : lastName(lastName), firstName(firstName) , food("apple") {
		cout << "Mother constructor called." << endl;
	}
	~Mother() {}
	void dance() {
		cout << "ballet dance." << endl;
	}
private:
	string lastName;
	string firstName;
	string food;
};

class Son : public Father, public Mother {
public:
	Son(const char* lastName = "None", const char* firstName = "None", const char* game = "None", const char* food = "None") : Father(lastName, firstName), Mother(lastName, firstName, food), game(game) {
	
	}
	~Son() {}
	void play() {
		cout << "Son play " << game << "." << endl;
	}
private:
	string game;
};

int main() {
	Son son("Liu", "Hong", "football", "apple");
	son.football();
	son.dance();
	son.play();
	return 0;
}

两个父类都有dance这个函数 派生类不知道该调用哪个

解决方法:

相关推荐
四维碎片2 分钟前
【Qt】QApplication::restoreOverrideCursor():恢复鼠标光标到原始状态的用法解析
开发语言·qt·计算机外设
远望清一色15 分钟前
基于小波变换图像去噪MATLAB实现
开发语言·matlab
秋说19 分钟前
【数据结构 | PTA】懂蛇语
数据结构·c++
黄交大彭于晏23 分钟前
第五天学习总结:C语言学习笔记 - 数组篇
c语言·笔记·学习
cl°24 分钟前
WPF中视觉树和逻辑树的区别和联系
经验分享·学习·c#·wpf
凯子坚持 c32 分钟前
String的长度有限,而我对你的思念却无限延伸
c++
啊QQQQQ33 分钟前
linux:回车换行+进度条+git理解与使用以及如何解决免密码push问题
开发语言·javascript·ecmascript
Ylucius40 分钟前
14天速成前端 ------学习日志(已完结)------ 后端程序员学习了解前端
java·开发语言·前端·vue.js·学习·状态模式·1024程序员节
就叫飞六吧1 小时前
关于Java中**optional,stream,lambda**
java·开发语言
何曾参静谧1 小时前
「C/C++」C++20 之 #include<ranges> 范围
c语言·c++·c++20