第八站:C++面向对象(继承和派生)

发布时间:2024年01月17日

继承和派生

?派生:由父类派生出子类

继承:子类继承父类(继承不会继承析构函数和构造函数:父类的所有成员函数,以及数据成员,都会被子类继承!)

"子类派生出的类"会指向"父类被继承的类",父类就是基类

实例1:

先创建一个父类,有私有成员数据(name,和age),成员函数,描述信息,有参的构造函数

再创建一个子类,有自己独立的私有成员(game),其余的继承自父类(注:无法继承构造函数)

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 describe();

private:
	string name;
	int age;
};

?father.cpp

#include "Father.h"
#include <sstream>
#include <iostream>

Father::Father(const char* name, int age) {
	cout << __FUNCTION__ << endl;
	this->name = name;
	this->age = age;
}
Father::~Father() {

}
string Father::getName()const {
	cout << __FUNCTION__ << endl;
	return name;
}
int Father::getAge()const {
	cout << __FUNCTION__ << endl;
	return age;
}
string Father::describe() {
	cout << __FUNCTION__ << endl;
	stringstream ret;
	ret << "姓名:" << name << " 年龄:" << age << endl;
	return ret.str();
}

?son.h

#pragma once
#include "Father.h"
class Son :public Father{//子类son继承父类father
public:
	//子类不会调用父类的构造函数
	Son(const char*name,int age,const char *game);
	~Son();
	string getGame()const;
	string describe();//继承了父类的成员函数,但是需要重写用来描述自己的信息
private:
	string game;
};

son.cpp

#include "Son.h"
#include <sstream>
#include <iostream>
//子类虽然没有继承父类的构造方法,
//但是在继承父类成员数据,进行自写构造方法时,
//会先调用父类的构造方法,进行初始化父类的成员数据,
//然后再调用自己的的构造函数,初始化自己的成员数据
//当没有体现出父类构造函数时,编译器会自动调用父类的默认构造函数
//const char* name, int age这一段就是子类应要调用父类的,需要手动添加
//:Father(name,age)//用于体现父类的构造函数
Son::Son(const char* name, int age, const char* game):Father(name,age) {
	cout << __FUNCTION__ << endl;
	this->game = game;
}
Son::~Son() {

}
string Son::getGame()const {
	cout << __FUNCTION__ << endl;
	return game;
}
string Son::describe() {
	cout << __FUNCTION__ << endl;
	stringstream ret;
	//这里的是无法直接调用name和age的,他们作为父类的私有成员数据
	// 子类虽然继承,但是无法调用,可以通过父类公有的get方法去获取私有的成员数据
	// 也可以使用protected关键字在父类进行说明
	//ret << "姓名:" << name << "年龄:" << age << endl;
	ret << "姓名:" << getName() << " 年龄:" << getAge() << " 游戏:" << game << endl;
	return ret.str();
}

知识点汇总:

1:子类构造函数

不存在默认构造函数

注意:无论创建几个对象,该类的静态成员只构建一次,所有静态成员的构造函数值调用一次

//子类虽然没有继承父类的构造方法,
//但是在继承父类成员数据,进行自写构造方法时,
//会先调用父类的构造方法,进行初始化父类的成员数据,
//然后再调用自己的的构造函数,初始化自己的成员数据
//当没有体现出父类构造函数时,编译器会自动调用父类的默认构造函数
//const char* name, int age这一段就是子类应要调用父类的,需要手动添加
//:Father(name,age)//用于体现父类的构造函数

?

子类父类构造函数调用顺序:
静态数据成员[ 只会调用一次 ]的(外类)构造函数 -> 父类的构造函数 -> 非静态的数据成员的(外类)构造函数 -> 自己的构造函数
#include <iostream>
#include <Windows.h>
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 ms;
};
N B::ms{}; //静态成员
int main(void) {
	B b;
	system("pause");
}
子类父类析构函数调用顺序:
静态数据成员[ 只会调用一次 ]的(外类)构造函数 <-?父类的构造函数 <- 非静态的数据成员的(外类)构造函数 <-?自己的构造函数

?

?2:子类重写函数

父类的成员数据无法访问

//这里的是无法直接调用name和age的,他们作为父类的私有成员数据
// 子类虽然继承,但是无法调用,可以通过父类公有的get方法去获取私有的成员数据

ret << "姓名:" << getName() << " 年龄:" << getAge() << " 游戏:" << game << endl;
// 也可以使用protected关键字在父类进行说明

?派生类子类对象的内存分布

?在命令行中添加选项:(打印指定类的内存分布)

成员函数不占内存空间,但是也被子类继承了!!!

//报告单个类的内存分布
/d1 reportSingleClassLayout Father
/d1 reportSingleClassLayout Son
通过sizeof打印他们各自占的内存字节数
cout << sizeof(father) << endl;
cout << sizeof(son) << endl;

?

?

?

?三种访问权限总结

public:外部和子类都可以直接访问

private:外部及子类都无法访问,本类的成员函数内可以访问

protected访问权限:外部无法访问,但是子类可以访问

在不适用protected的前提下,子类访问父类的成员数据,只能通过父类的get成员函数,来访问

但是使用protected访问权限后,子类可以直接访问父类的成员数据,父类的对象依然无法访问成员数据

三种继承方式;

public继承方式:

#include "Father.h"
class Son :public Father

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

public --->public

private--->private

protected---->pritected?

?private继承方式

#include "Father.h"
class Son :private Father

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

public --->private

private--->private

protected---->private?

?protected继承方式

#include "Father.h"
class Son :protected Father

protected 继承 只把 public 降级为 protected

public --->protected

private--->private

protected---->private?

?子类型(public继承)

子类继承父类,是父类的派生,子类的对象可以作为基类的对象(形参是基类对象时,实参可以用子类对象)

1. 基类(父类)的指针,可以指向这个类的公有派生类(子类型)对象。
????????Son b ;
????????Father * a?= &b;
2. 公有派生类(子类型)的对象可以初始化基类的引用
????????Son b ;
????????Father &a?= b;
3. 公有派生类的对象可以赋值给基类的对象
????????Son b ;
????????Father a ?= b;

?

#include <iostream>
#include <Windows.h>
using namespace std;
class A {
public :
	A() {};
	void kill() {
		cout << "父类杀敌" << endl;
	}
	
};
class B :public A {
public:
	B() {};
	void kill() {
		cout << "子类杀敌" << endl;
	}
};
void test(A a) {
	a.kill();
}
int main(void) {
	B b;//将子类的对象传给父类的对象
	test(b);
}

多重继承

概念:

将多个基类用逗号隔开.调用基类的顺序和声明基类出现的顺序一致

class D: public A, private B, protected C{
????????//类 D 自己新增加的成员
};?
D 是多继承形式的派生类,
D 3 个父类 ( 基类 )
它以公有的方式继承 A 类,
以私有的方式继承 B 类,
以保护的方式继承 C 类。
D 根据不同的继承方式获取 A B C 中的成员 .

?弊端:

当子类继承的多个基类,中的方法有相同的时候,就会出现二义性,但是可以通过基类名指定调用哪一个基类的方法

Class A{
? ? ? ? void dance(){};
};
Class B{
? ? ? ? void dance(){};
};
class D: public A, public B{
????????//类 D 自己新增加的成员
解决二义性方法2:
? ? ? ? void dance(){
????????????????B::dance();
? ? ? ? ? ? ? ? A::dance();
????????};
};?
int main(void){
? ? ? ? D d;
? ? ? ? d.dance();//这时候就不知道调用谁的方法了
解决二义性方法1:
? ? ? ? d.A::dance();//通过基类名::方法,指定调用哪一个
? ? ? ? d.B::dance();
解决二义性方法2:
? ? ? ? 在子类中,指定,( 美观)
? ? ? ? d.dance();//调用自己指定好的,进行调用
}

问题:

但是当子类继承的是多个子类(这些子类继承同一个基类)的时候,就容易出现,指定不明确的时

?public:
?? ?//类 D 自己新增加的
?? ?void setTel(string Tel) {
?? ??? ?this->A::tel = Tel;
?? ?};
?? ?string getTel() {
?? ??? ?return B::tel;
?? ?};

#include <iostream>
#include <string>
using namespace std;
class ALL {
public:
	ALL() {
		this->tel = "未知";
	}
protected:
	string tel;
};
class A : public ALL{
	
};
class B : public ALL{
	
};
class D : public A, public B {
public:
	//类 D 自己新增加的
	void setTel(string Tel) {
		this->A::tel = Tel;
	};
	string getTel() {
		return B::tel;
	};
};
int main(void) {
	D d;
	d.setTel("1234678");
	cout << d.getTel() << endl;
	system("pause");
	return 0;
}

解决二义性弊端问题

使用虚基类和虚继承

此时ALL是虚基类? B,C是虚继承(virtual)

class A : virtual?public ALL{?
};
class B :virtual public ALL{
};

??public:
?? ?//类 D 自己新增加的
?? ?void setTel(string Tel) {
?? ??? ?this->tel = Tel;
?? ?};
?? ?string getTel() {
?? ??? ?return tel;
?? ?};

#include <iostream>
#include <string>
using namespace std;
class ALL {
public:
	ALL() {
		this->tel = "未知";
	}
protected:
	string tel;
};
class A : virtual public ALL{
	
};
class B : virtual public ALL{
	
};
class D : public A, public B {
public:
	//类 D 自己新增加的
	void setTel(string Tel) {
		this->tel = Tel;
	};
	string getTel() {
		return tel;
	};
};
int main(void) {
	D d;
	d.setTel("1234678");
	cout << d.getTel() << endl;
	system("pause");
	return 0;
}

继承小练习:

练习1:

定义一个Book类

查看当前价格 .
查看当前的书号
定义一个类 SellBook, 用来表示促销的书籍 , 要求继承自 Book
具有以下功能 :
1. 查看当前折扣
2. 设置当前折扣
3. 查看当前的促销价格

?.h

Book.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Book
{
public:
	Book(float price ,string isbn);
	~Book();
	float getPrice()const;
	string getIbsn() const;
	void setPrice(float price);
	void setIsbn(string ISBN);
protected:
	float price;
	string IBSN;
};


SellBook.h
#pragma once
#include "Book.h"
class SellBook :public Book{
public:
	SellBook(float price,string isbn,float discount = 1.0);
	~SellBook();
	float getDiscount()const;
	void setDiscount(float discount);
	void setPrice(float price);
	float getPrice()const ;

private:
	float discount;
};

.cpp

Book.cpp
#include "Book.h"

Book::Book(float price, string isbn)
{
	this->price = price;
	this->IBSN = isbn;
}
Book::~Book(){

}
float Book::getPrice()const {
	return price;
}
string Book::getIbsn()const {
	return IBSN;
}
void Book::setPrice(float price) {
	this->price = price;
}
void Book::setIsbn(string isbn) {
	this->IBSN = isbn;
}

SellBook.cpp
#include "SellBook.h"

SellBook::SellBook(float price, string isbn, float discount):Book(price,isbn)
{
	this->discount = discount;
}
SellBook::~SellBook() {

}
float SellBook::getDiscount()const {
	return discount;
}
void SellBook::setDiscount(float discount) {
	this->discount = discount;
}
float SellBook::getPrice() const
{
	return price*discount;
}


main.cpp
#include "Book.h"
#include "SellBook.h"
#include <iostream>
using namespace std;

int main(void) {
	SellBook book1(1200,"123456-123");
	cout << "未折扣前价格: " << book1.getPrice()
		<< "书号:" << book1.getIbsn() << endl;
	cout << endl;
	float n = 0.0;
	cout << "请输入当前的折扣力度: ";
	cin >> n;
	book1.setDiscount(n);
	cout << "当前折扣为: " << book1.getDiscount() << endl;
	cout << "折扣后价格: " << book1.getPrice()
		<< "书号:" << book1.getIbsn() << endl;
	system("pause");
	return 0;
}

?练习2:

某无线通信设备 ODU 设备:
????????查看发射功率, 设置发射功率 ,
????????查看发射频率, 设置发射频率 ,
????????查看带宽, 修改带宽 ,
????????查看设备概述( 各指标的值 )
对该产品做了升级 , 研发了 ODU330 产品 :
????????查看当前的误码率.
????????查看误码率告警门限
????????设置误码率告警门限

?.h

ODU.h
#pragma once
#include <string>
using namespace std;
class ODU{
public:
	~ODU();
	ODU();
	int getToPower()const;
	int getToFreq()const;
	int getBandWidth()const;
	void setToPower(int toPower);
	void setToFreq(int toFreq);
	void setBandWidth(int bandWidth);
	string describe();
protected:
	int toPower;
	int toFreq;
	int bandWidth;
};
ODU330.H
#pragma once
#include "ODU.h"
class ODU330 :public ODU{
public:
	ODU330();
	~ODU330();
	float getERate()const;
	float getThresHold()const;
	void setThresHold(float thresHold);
	string describe();
private:
	float ERate = 0.003f;
	float thresHold;
};

.CPP

ODU.CPP
#include "ODU.h"
#include <sstream>
using namespace std;
ODU::ODU() {

}
ODU::~ODU() {

}
int ODU::getToPower()const {
	return toPower;
}
int ODU::getToFreq()const {
	return toFreq;
}
int ODU::getBandWidth()const {
	return bandWidth;
}
void ODU::setToPower(int toPower) {
	this->toPower = toPower;
}
void ODU::setToFreq(int toFreq) {
	this->toFreq = toFreq;
}
void ODU::setBandWidth(int bandW) {
	this->bandWidth = bandW;
}
string ODU::describe() {
	stringstream ret;
	ret << "当前发射功率为: " << toPower << " 当前发射频率为: " << toFreq
		<< " 当前带宽为: " << bandWidth << endl;
	return ret.str();
}
ODU.CPP
#include "ODU330.h"
#include <sstream>
ODU330::ODU330() {

}
ODU330::~ODU330() {
}
float ODU330::getERate()const {
	return ERate;
}
float ODU330::getThresHold()const {
	return thresHold;
}
void ODU330::setThresHold(float thresHold) {
	this->thresHold = thresHold;
}
string ODU330::describe(){
	stringstream ret;
	ret << "当前发射功率为: " << toPower << " 当前发射频率为: " << toFreq
		<< " 当前带宽为: " << bandWidth << "\n"
		<< "新增功能: " << "误码率为: " << ERate << " 告警门限为: " << thresHold <<
		endl;
	return ret.str();
}
main.cpp
#include "ODU.h"
#include "ODU330.h"

#include <iostream>
using namespace std;

int main(void) {
	ODU h1;
	h1.setBandWidth(600);
	h1.setToPower(45);
	h1.setToFreq(115000);
	cout << h1.describe() << endl;

	ODU330 h2;
	h2.setBandWidth(900);
	h2.setToPower(48);
	h2.setToFreq(119000);
	h2.setThresHold(0.00001);
	cout <<h2.describe()<<endl;
}
文章来源:https://blog.csdn.net/qq_48397625/article/details/135616024
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。