?本博客将记录:类的相关知识点的第4节的笔记!
(这个在学习C++基础课程时已经学习过一次了,这里再次简单地回顾一下而已)
今天总结的知识分为以下3个点:
? 一、派生类的概念 1)派生类对象定义时调用构造函数的顺序? ? and? ?2)派生类对象销毁时调用构造函数的顺序 ? 二、public protected private 三种继承方式(访问权限)(详细总结,配案例代码) ? 三、函数遮蔽
? 一、派生类derived class(子类)的概念:
????????类与类之间,有一种层次的关系,也即:父类和孩子类的关系。父亲和孩子类之间的这种关系我们称之为继承。继承,是面向对象的程序设计的核心思想之一!
????????继承的语句格式:
class 子类名 : 继承方式 父类名{
public:
/.../
};
????????我们举个例子把,车Car这个类我们称之为父类(也称之为基类or超类),由该类派生出来的卡车电动车等类,就称之为子类(派生类)。说白了,所谓的继承二字,就是我们先定义一个父类,而该父类中定义了一些公用的成员变量以及成员函数。通过(public/private/protected)继承父类来创建新的子类。一般而言,我们在继承之后只需要在子类中重写与父类同名的函数以及virtual虚函数,再编写属于子类独有的成员变量和成员函数即可。
1)派生类(子类)对象定义时调用构造函数的顺序:
? ? ? ? 当我们创建子类对象时,编译器会为我们先调用其父类的构造函数,用于创建子类对象中的父类成分(也即父类的成员变量),然后再调用子类的构造函数用于创建子类的成员变量。
(也即,我们在创建子类的对象时会先执行父类的构造函数的函数体创建父类的成分,然后再执行子类的构造函数的函数体创建子类的成分。)
2)派生类(子类)对象销毁时调用构造函数的顺序:
? ? ? ? 当我们销毁子类对象时,编译器会为我们先调用子类的析构函数,用于销毁子类的对象以及子类的成员变量,然后再调用父类的析构函数用于销毁父类的对象以及父类的成分(也即父类的成员变量)
(也即,我们在销毁子类的对象时会先执行子类的析构函数的函数体销毁子类的成分,然后再执行父类的析构函数的函数体销毁父类的成分。)
直接请看以下代码:
Human.h
#ifndef __HUMAN_H__//这是头文件的防卫声明
#define __HUMAN_H__
#include<iostream>
#include<string>
using namespace std;
//声明基类/父类/超类
class Human {
public:
int m_Age;
string m_Name;
public:
Human() :m_Age(0), m_Name("") {
cout << "默认的Human()无参构造函数!" << endl;
}
Human(int age,string str) :m_Age(age), m_Name(str) {
cout << "有参Human(int age,string str)构造函数!" << endl;
}
~Human() {
cout << "默认的~Human()无参析构函数!" << endl;
}
};
#endif __HUMAN_H__
Man.h
#ifndef __MAN_H__//这是头文件的防卫声明
#define __MAN_H__
#include<iostream>
#include"Human.h"
using namespace std;
//声明子类/派生类
class Man :public Human {//表示Man为Human的子类
public:
Man(){ cout << "默认的Man()无参构造函数!" << endl; }
~Man(){ cout << "默认的~Man()析构函数!" << endl; }
};
#endif __MAN_H__
main.cpp
#include <iostream>
#include"Human.h"
#include"Man.h"
using namespace std;
void test() {
Man m;//创建子类对象
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
继承的注意事项:
????????当然,你也可以进行多继承,只不过我们在实际的开发中并不推荐你去do多继承这样的事情!
二、public protected private 三种继承方式(访问权限):
????????这是重要的知识点,我给大家画一张图来总结一下!学习这三个关键字只搞定下面这张图完全足够了!并且,以后但凡是遇到这三个关键字,你如果忘记了其对应的访问特性,可以立马回来查表!
????????相信大家一开始看到我总结的这个图,都很懵逼,那么现在我就用代码来给大家解释一下:
????????注意:这里我都是以三种访问权限的成员变量do为写代码的具体例子,对于三种访问权限的成员函数也是一样的效果,一样的访问特点!
公有继承代码:
Human.h
#ifndef __HUMAN_H__//这是头文件的防卫声明
#define __HUMAN_H__
#include<iostream>
#include<string>
using namespace std;
//声明基类/父类/超类
class Human {
public://公有成员变量
int m_Age;//年龄
string m_Name;//姓名
protected://保护成员变量
string m_Disease;//疾病
private://私有成员变量
int m_FriendNums;//朋友数量
public:
Human() :m_Age(0), m_Name(""), m_Disease("无"), m_FriendNums(0) {
cout << "调用默认的Human无参构造函数!" << endl;
}
Human(int age,string str,string disease,int frinums) :m_Age(age), m_Name(str),
m_Disease(disease), m_FriendNums(frinums){
cout << "调用有参Human构造函数!" << endl;
}
~Human() {
cout << "调用默认的~Human析构函数!" << endl;
}
};
#endif __HUMAN_H__
Man.h
#ifndef __MAN_H__//这是头文件的防卫声明
#define __MAN_H__
#include<iostream>
#include"Human.h"
using namespace std;
//声明子类/派生类
class Man :public Human {//Man是public公有继承自Human的
//父类的public公有成员变量 在子类中仍然为public的!
/* public:
int m_Age;
string m_Name; */
//父类的protected保护成员变量 在子类中仍然为protected的!
/* protected:
string m_Disease; */
//父类的private私有成员变量 在子类永远无法访问!
/* int m_FriendNums; */ 无法访问!
public:
Man(){
cout << "姓名:" << this->m_Age;
cout << " 年龄:" << this->m_Name;
cout << " 疾病:" << this->m_Disease;
//cout << " 朋友数量:" << this->m_FriendNums << endl;
//报错!父类的私有成员在任何方式的继承下都不允许被访问!
cout << "\n调用默认的Man无参构造函数!" << endl;
}
~Man() { cout << "调用默认的~Man析构函数!" << endl; }
};
#endif __MAN_H__
main.cpp
#include <iostream>
#include"Human.h"
#include"Man.h"
using namespace std;
void test() {
Man m;
}
int main(){
test();
system("pause");
return 0;
}
运行结果:
保护继承代码:(只需要在上述代码中把Man.h中的public改为proteced)
Man.h
class Man : protected Human {//Man是protected保护继承自Human的
//父类的public公有成员变量 在子类中为protected的!
/* protected:
int m_Age;
string m_Name; */
//父类的protected保护成员变量 在子类中为protected的!
/* protected:
string m_Disease; */
//父类的private私有成员变量 在子类永远无法访问!
/* int m_FriendNums; */ 无法访问!
public:
Man(){
cout << "姓名:" << this->m_Age;
cout << " 年龄:" << this->m_Name;
cout << " 疾病:" << this->m_Disease;
//cout << " 朋友数量:" << this->m_FriendNums << endl;
//报错!父类的私有成员在任何方式的继承下都不允许被访问!
cout << "\n调用默认的Man无参构造函数!" << endl;
}
~Man() { cout << "调用默认的~Man析构函数!" << endl;}
};
运行结果:
?私有继承代码:(只需要在上述代码中把Man.h中的proteced改为private)
Man.h
class Man : private Human {//Man是private 私有继承自Human的
//父类的public公有成员变量 在子类中为private的!
/* private:
int m_Age;
string m_Name; */
//父类的protected保护成员变量 在子类中为private的!
/* private:
string m_Disease; */
//父类的private私有成员变量 在子类永远无法访问!
/* int m_FriendNums; */ 无法访问!
public:
Man(){
cout << "姓名:" << this->m_Age;
cout << " 年龄:" << this->m_Name;
cout << " 疾病:" << this->m_Disease;
//cout << " 朋友数量:" << this->m_FriendNums << endl;
//报错!父类的私有成员在任何方式的继承下都不允许被访问!
cout << "\n调用默认的Man无参构造函数!" << endl;
}
~Man() { cout << "调用默认的~Man析构函数!" << endl;}
};
运行结果:
??从运行结果可见,公有继承、保护继承、私有继承时,父类的成员变量均可用在子类中!!!
下面我们再来看一看,对于一个单独的类,其public、protected、private成员(这里我以成员变量为例子)在类外的访问特点是如何的呢?
请看以下代码:
????????这里为了方便起见,将成员变量设置为static静态的,以便于我在类外用类名::static静态成员变量名的方式直接来访问类内的静态变量!因为静态成员是跨对象的!
????????这一小知识点在前几小节我总结static关键字时已经总结地很清楚了,不知道的小伙伴可以翻阅我前面的总结。
C++新经典课程学习笔记之第三章-3.3小节(重要)
#ifndef __HUMAN_H__//这是头文件的防卫声明
#define __HUMAN_H__
#include<iostream>
#include<string>
using namespace std;
//声明基类/父类/超类
class Human {
public://公有成员
static int m_Age;//年龄
static string m_Name;//姓名
protected://保护成员
static string m_Disease;//疾病
private://私有成员
static int m_FriendNums;//朋友数量
};
void func() {
cout << Human::m_Age<< endl;//类外可以访问类内的public成员!
cout << Human::m_Name << endl;//类外可以访问类内的public成员!
//cout << Human::m_Disease << endl;//报错!类外不可以访问类内的protected成员!
//cout << Human::m_FriendNums << endl;//报错!类外不可以访问类内的private成员!
}
#endif __HUMAN_H__
?相信通过这一个点的详述,你已经学习并清楚地知道了一个类中的public、protected、private三种访问权限(属性)在类外的访问特点了。即:
①public访问权限下的成员可以给任意类内外的实体访问!
②protected访问权限下的成员只可以给类内和子类的成员函数来访问!
③private访问权限下的成员只可以给类内的成员来访问!
? 三、函数遮蔽:
? ? ? ? 所谓的函数遮蔽,其实就是当父类中重载了一些同名函数时,子类若也重写了该同名函数的话,那么不论父类中重载了几个这样的同名函数,其在子类中都无法访问到了!
请看以下代码:
Human.h
#ifndef __HUMAN_H__//这是头文件的防卫声明
#define __HUMAN_H__
#include<iostream>
#include<string>
using namespace std;
//声明基类/父类/超类
class Human {
public://公有成员
int m_Age;//年龄
string m_Name;//姓名
protected://保护成员
string m_Disease;//疾病
private://私有成员
int m_FriendNums;//朋友数量
public:
Human() :m_Age(0), m_Name(""), m_Disease("无"), m_FriendNums(0) {
cout << "调用默认的Human无参构造函数!" << endl;
}
Human(int age,string str,string disease,int frinums) :m_Age(age), m_Name(str),
m_Disease(disease), m_FriendNums(frinums){
cout << "调用有参Human构造函数!" << endl;
}
~Human() { cout << "调用默认的~Human析构函数!" << endl;}
void func() { cout << "调用父类Human的func()" << endl; }
void func(int) { cout << "调用父类Human的func(int)" << endl; }
};
#endif __HUMAN_H__
Man.h
#ifndef __MAN_H__//这是头文件的防卫声明
#define __MAN_H__
#include<iostream>
#include"Human.h"
using namespace std;
//声明子类/派生类
class Man : public Human {//public 公有继承自Human
public:
Man(){ cout << "\n调用默认的Man无参构造函数!" << endl;}
~Man() { cout << "调用默认的~Man析构函数!" << endl;}
//void func() { cout << "调用子类Man的func()" << endl; }
};
#endif __MAN_H__
main.cpp
#include <iostream>
#include"Human.h"
#include"Man.h"
using namespace std;
void test() {
Man m;
m.func();
m.func(1);
}
int main(){
test();
return 0;
}
运行结果:
?但是,如果你在子类Man.h中重写了父类中的同名函数的话:
#ifndef __MAN_H__//这是头文件的防卫声明
#define __MAN_H__
#include<iostream>
#include"Human.h"
using namespace std;
//声明子类/派生类
class Man : public Human {//public 公有继承自Human
public:
Man(){ cout << "\n调用默认的Man无参构造函数!" << endl;}
~Man() { cout << "调用默认的~Man析构函数!" << endl;}
//重写父类中的同名函数
void func() { cout << "调用子类Man的func()" << endl; }
};
#endif __MAN_H__
?编译就会不通过!报错!这就是所谓的函数遮蔽!
|