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

继承和派生

派生:由父类派生出子类

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

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

实例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;
}
相关推荐
天下皆白_唯我独黑7 分钟前
php 使用qrcode制作二维码图片
开发语言·php
夜雨翦春韭10 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
小远yyds12 分钟前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
何曾参静谧24 分钟前
「C/C++」C/C++ 之 变量作用域详解
c语言·开发语言·c++
AI街潜水的八角34 分钟前
基于C++的决策树C4.5机器学习算法(不调包)
c++·算法·决策树·机器学习
q567315231 小时前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
JSU_曾是此间年少1 小时前
数据结构——线性表与链表
数据结构·c++·算法
许野平1 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
也无晴也无风雨1 小时前
在JS中, 0 == [0] 吗
开发语言·javascript
狂奔solar1 小时前
yelp数据集上识别潜在的热门商家
开发语言·python