Review cpp day08

Posted 达少Rising

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Review cpp day08相关的知识,希望对你有一定的参考价值。

回顾:
Review cpp day01
Review cpp day02
Review cpp day03
Review cpp day04
Review cpp day05
Review cpp day06
Review cpp day07

二十一、继承(Inheritance)

1、继承的概念

  • 通过一种机制表达类型之间共性和特性的方式,利用已有的数据类型定义新的数据类型,这种机制就是继承。
    eg:
人类:姓名、年龄、吃饭、睡觉
学生类:姓名、年龄、吃饭、睡觉、学号、学习
教师类:姓名、年龄、吃饭、睡觉、工资、授课
---------------------------------------------------
人类:姓名、年龄、吃饭、睡觉
学生类继承人类:学号、学习
教师类继承人类:工资、授课
	           人类(基类、父类)
	            /         \\
           学生类	      教师类(派生类、子类)

注: 基类派生子类,子类继承基类

2、继承语法

class 子类:继承方式 基类,......{
	.......
};
  • 继承方式:
    • public:公有继承
    • protected:保护继承
    • private:私有继承

eg:

class Humen{姓名、年龄、吃饭、睡觉};
class Student:public Human{
	//学生类继承了人类
	//人类中的成员在当前学上类就可以直接使用
};

01inhert.cpp

#include <iostream>
using namespace std;

//人类
class Human{
public:
	Human(string name, int age):m_name(name), m_age(age){}
	void eat(const string& food){
		cout << "我在吃" << endl;
	}
	void sleep(int time){
		cout << "我睡了" << time << "小时" << endl;
	}
protected://保护成员可以在子类中被访问
	string m_name;
	int m_age;
};
//学生类(人类的一个子类)
class Student : public Human{
public:
	//Human(...)是从基类继承过来的成员初始化方式
	Student(const string& name, int age, int no) : Human(name, age), m_no(no){}
	void learn(const string& course){
		cout << "我在学" << coursr << endl;
	}
	void who(void){
		cout << "我叫" << m_name << ",今年" << m_age << "岁,学号是" << m_no << endl;
	}
private:
	int m_no;
};
//教师类(人类的另一个子类)
class Teacher : public Human{
public:
	Teacher(const string& name, int age, double salary) : Human(name, age), m_salary(salary){}
	void teach(const string& course){
		cout << "我在讲" << course << endl;
	}
	void who(void){
		cout << "我叫" << m_name << ",今年" << m_age << "岁,工资是" << m_salary << endl;
	}
private:
	double m_salary;
};

int main(void){
	Student s("关羽", 35, 10011);
	s.who();
	s.eat("牛肉面");
	s.sleep(8);
	s.learn("孙子兵法");

	Teacher t("孙悟空", 30, 50000);
	t.who();
	t.eat("桃子");
	t.sleep(6);
	t.teach("春秋刀法");

	return 0;
}

3、公有继承的特性(public)******

  • 1)子类对象会继承基类属性的行为,通过子类对象可以访问基类中的成员,如同是基类对象在访问它们一样;
    注: 子类对象中包含基类的部分称为“基类子对象”
  • 2)向上造型:将子类类型的指针或引用转换成基类类型的指针或引用
    注: 这种操作性缩小的类型的转换,在编译器看来是安全的可以直接隐式完成转换。
	  基类
  	   ⬆
	  子类

eg:

	class Base{...};//基类
	class Derived : public Base{...};//子类

	void func1(Base& ){}
	void func2(Base* ){}
	
	int main(){
		Derived d;
		func1(d);//向上造型
		func2(&d);//向上造型
	}
  • 3)向下造型:将基类类型的指针或引用转换为子类类型的指着或引用;
	  基类
	   ⬇
	  子类

注: 这种操作性扩大的类型的转换,在编译器看来是危险的不可以直接隐式完成转换,但可以显示转换。

  • 4)子类继承基类的成员
    • 在类中,可以直接访问基类中的公有或保护成员,如同它们是子类自己的成员一样;
    • 基类中的私有成员子类也可以继承过来,但是会受到访问控制属性的限制,无法直接访问,如果子类需要访问基类中的私有成员,可以通过基类提供的公有或保护的接口函数来访问。
    • 基类的构造函数和析构函数,子类是无法继承的,但是可以在子类的构造函数使用初始化表显式指明基类部分的初始化方式。

01inhert.cpp

#include <iostream>
using namespace std;

//人类
class Human{
public:
	Human(string name, int age):m_name(name), m_age(age), m_id(123456){}
	void eat(const string& food){
		cout << "我在吃" << endl;
	}
	void sleep(int time){
		cout << "我睡了" << time << "小时" << endl;
	}
protected://保护成员可以在子类中被访问
	string m_name;
	int m_age;
	//私有成员子类无法直接访问,但是可以提供保护的接口函数来间接访问
	const int& getId(void){
		return m_id;
	}
private:
	int m_id;
};
//学生类(人类的一个子类)
class Student : public Human{
public:
	//Human(...)从基类中继承过来的成员初始化方式
	Student(const string& name, int age, int no) : Human(name, age), m_no(no){}
	void learn(const string& course){
		cout << "我在学" << coursr << endl;
	}
	void who(void){
		cout << "我叫" << m_name << ",今年" << m_age << "岁,学号是" << m_no << endl;
		cout << "身份证号:" << getId() << endl;//间接访问基类私有成员
	}
private:
	int m_no;
};
//教师类(人类的另一个子类)
class Teacher : public Human{
public:
	Teacher(const string& name, int age, double salary) : Human(name, age), m_salary(salary){}
	void teach(const string& course){
		cout << "我在讲" << course << endl;
	}
	void who(void){
		cout << "我叫" << m_name << ",今年" << m_age << "岁,工资是" << m_salary << endl;
	}
private:
	double m_salary;
};

int main(void){
	Student s("关羽", 35, 10011);
	s.who();
	s.eat("牛肉面");
	s.sleep(8);
	s.learn("孙子兵法");

	Teacher t("孙悟空", 30, 50000);
	t.who();
	t.eat("桃子");
	t.sleep(6);
	t.teach("春秋刀法");

	//Student*-->Human*:向上造型
	Human* ph = &s;
	ph->eat("苹果");
	ph->sleep(6);
	//ph->learn("编程")//error

	//Human*->Student*:向下造型(安全的)
	//不能隐式转换,但可以显示转换
	Student* ps = static_cast<student*>(ph);
	ps->who();

	Human h("林黛玉", 20);
	//Human*->student*:向下造型(危险的)
	Student* ps2 = static_cast<Student*>(&h);
	ps2->who();

	return 0;
}
  • 5)子类隐藏基类的成员
    • 子类和基类中有同名的成员函数,因为作用域不同,不会有重载关系,而是一种隐藏关系。如果需要访问基类中隐藏的成员函数,可以通过“基类::”显式指明。
    • 如果隐藏的成员函数满足同名不同参重载条件,也可以通过using声明的方式,将基类的成员函数引入到子类的作用域,让它们形成重载关系,通过重载匹配来解决(//不推荐这种方式)

02inhert.cpp

class Base{
public:
	void foo(void){
		cout << "Base::foo" << endl;
	}
};
class Derived: public Base{
public:
	void foo(int i){
		cout << "Derived::foo" << endl;
	}
	//将基类foo函数引入当前子类作用域
	//using Base::foo;
};
int main(void){
	Derived d;
	d.Base::foo();
	d.foo(10);
	return 0;
}

4、继承方式和访问控制属性

  • 1)访问控制限定符:影响访问该成员的位置
访问控制限定符访问控制属性内部访问子类访问外部访问友元访问
public公有成员okokokok
protected保护成员okoknook
private私有成员oknonook
  • 2)基类继承方式:影响通过子类访问基类中的成员的可访问性
基类中的在公有子类中变成在保护子类中变成在私有子类中变成
公有成员公有成员保护成员私有成员
保护成员保护成员保护成员私有成员
私有成员私有成员私有成员私有成员

注: 向上造型的语法在保护继承或私有继承中不再使用

03inhert.cpp

#include <iostream>
using namespace std;

class A{
public:
	int m_public;
protected:
	int m_protected;
private:
	int m_private;
};
class B:public A{};
class C:protected A{};
class D:private A{};

class X:public B{
public:
	void func(void){
		m_public =10;
		m_pretected = 10;
		//m_private = 10;//error
	}
};
class Y:public C{
public:
	void func(void){
		m_public = 10;
		m_protected = 10;
		//m_private = 10;
	}
};
class Z:public D{
public:
	void func(void){
		//m_public = 10;
		//m_protected = 10;
		//m_private = 10;
	}
};
int main(void){
	B b;
	b.m_public = 10;
	//b.m_protected =10;//error
	//b.m_private = 10;//error

	C c;
	//c.m_public = 10;//error
	//c.m_protected = 10;//error
	//c.m_private = 10;error

	D d;
	//d.m_public =10;//error
	//d.m_protected = 10;error
	//d.m_private = 10;//error
	return 0; 
}

5、子类构造函数

  • 1)如果子类构造函数没有指明基类部分(基类子对象)的初始化方式,那么编译器将会调用基类的无参构造函数来初始化;
  • 2)如果希望基类子对象以有参的方式被初始化,需要在子类构造函数的初始化表中指明其初始化方式:
    class 子类:public 基类{
    	//基类(...):指明基类子对象的初始化方式
    	子类(...):基类(...){}
    };
    
  • 3)子类对象的构造过程
    • 分配内存
    • 构造基类子对象(按继承表顺序)
    • 构造成员子对象(按声明的顺序)
    • 执行子类的构造函数代码

04inhert.cpp

#include <iostream>
using namespace std;

class Base{
public:
	Base(void):m_i(0){
		cout << "Base::Base(void)" << endl;
	}
	Base(int i):m_i(i){
		cout << "Base::Base(int)" << endl;
	}
	int m_i;
};	
class Derived:public Base{
public:
	Derived(void){
		cout << "Derived::Derived(void)" << endl;
	}
	//Base(i):指明基类子类像初始化方式
	Derived(int i):Base(i){
		cout << "Derived::Derived(int)" << endl;
	}
};

int main(void){
	Derived d1;
	cout << d1.m_i << endl;//0
	Derived d2(123);
	cout << d2.m_i << endl;//123

	return 0;
}

6、子类的析构函数

  • 1)子类的析构函数,无论是自己定义的还是编译器缺省提供的,都会自动调用基类的析构函数, 析构基类子对象;
  • 2)子类对象的析构过程
    • 执行子类析构函数代码
    • 析构成员子对象(按声明的逆序)
    • 析构基类子对象(按继承表的逆序)
    • 释放内存

05inhert.cpp

#include <iostream>
using namespace std;
class Member{
public:
	Member(void):m_(0){
		cout << "Member::Member(void)" << endl;	
	}
	Member(int i):m_i(i){
		cout << "Member::Member(int)" << endl;
	}
	~Member(void){
		cout << "Member::~Member()" << endl;
	}
	int m_i;
};
class Base{
public:
	Base(void):m_i(0){
		cout << "Base::Base(void)" << endl;
	}
	Base(int i):m_i(i){
		cout << "Base::Base(int)" << endl;
	}
	~Base(void){
		cout << "Base::~Base()" << endl;
	}
	int m_i;
};	
class Derived:public Base{
public:
	Derived(void){
		cout << "Derived::Derived(void)" << endl;
	}
	//Base(i):指明基类子类像初始化方式
	//m_m(i):指明成员子对象初始化方式
	Derived(int i):Base(i), m_m(i){
		cout << "Derived::Derived(int)" << endl;
	}
	~Derived(void){
		cout << "Derived::~Derived(void)" << endl;
	}
	Member m_m;//成员子对象
};

int main(void){
	Derived d1;
	cout << d1.m_i << "," << d1.m_m << endl;//0,0
	/*Derived d2(123);
	cout << d2.m_i << "," << d2.m_m << endl;//123,123*/
	
	return 0;
}
Base::Base(void)
Member::Member(void)
Derived::Derived(void)
0,0
Derived::~Derived()
Member::~Member()
Base::~Base()
  • 3)基类的析构函数不会自动调用子类的析构函数。如果一个指向针子对象的基类指针使用delete操作符,实际被执行的仅是基类的析构函数,子类的析构函数执行不到,有内存泄漏的风险。
    class A{.....};
    class B:public A{......};
    A* pa = new B;//pa:指向子类对象的基类指针
    delete pa;//内存泄漏
    

07inherit.cpp

#include<iostream>
using namespace std;

class Base{
public:
	Base(void){
		cout << "基类动态资源的分配" << endl;
	}
	~Base(void){
		cout << "基类动态资源的释放" << endl;
	}
};

class Derived::public Base{
public:
	Derived(void){
		cout << "子类动态资源的分配" << endl;
	}
	~Derived(void){
		cout << "子类动态资源的释放" << endl;
	}
};
int main(void){
	//pb称为指向子类对象的基类指针
	Base* pb = new Derived;
	//......
	//只调用基类的析构函数,有内存泄漏风险
	delete pb;
	return 0;
}
基类动态资源的分配
子类动态资源的分配
基类动态资源的释放

7、子类的拷贝构造和拷贝赋值