继承和派生
派生:由父类派生出子类
继承:子类继承父类(继承不会继承析构函数和构造函数:父类的所有成员函数,以及数据成员,都会被子类继承!)
"子类派生出的类"会指向"父类被继承的类",父类就是基类
实例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继承)
子类继承父类,是父类的派生,子类的对象可以作为基类的对象(形参是基类对象时,实参可以用子类对象)
- 基类(父类)的指针,可以指向这个类的公有派生类(子类型)对象。
Son b ;
Father * a = &b;- 公有派生类(子类型)的对象可以初始化基类的引用
Son b ;
Father &a = b;- 公有派生类的对象可以赋值给基类的对象
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 类
具有以下功能 :
- 查看当前折扣
- 设置当前折扣
- 查看当前的促销价格
.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;
}