1.继承

  注意点1:继承限定符的作用,如果是采用public的继承方式,那么父类中数据成员或者是函数成员的访问修饰符会被带到子类中,及父类中的private类型数据在子类中不可访问,如果采用protected的继承方式,那么父类中的public型的数据成员或者函数成员会在子类中被降为protected,如果是private的话,那么父类中所有的public, protected, private成员被继承到子类中时都会变成private,在类外不可访问,且父类中的private成员在子类中也不可访问,示例代码如下:

#include "stdafx.h"
#include<iostream>
#include<typeinfo>
using namespace std;

class A {
public:
A():a(12),b(13){
cout << "这是A的构造函数" << endl;
}

~A()
{
cout << "这是A类的析构函数" << endl;
}
void show() {
cout << "this is a A class" << endl;
}
int b;
private:
int a;
};

class B :public A {
public:
B() {
cout << "这是B的构造函数" << endl;
}
~B()
{
cout << "这是B类的析构函数" << endl;
}
void show() {
cout<<a<<" "<<b; //编译器报错,因为a在A类中是私有的,无法在子类中进行访问
cout << "this is a B class" << endl;
}
};

int main() {
B b;
cout<< b.a; //编译器报错,因为a在A类中是私有的,无法在子类中进行访问
cout << b.b; //正确,因为b在A类中是public,并且通过public方式继承,所以可以访问
b.show();
return 0;
}

注意点2:关于构造函数和析构函数和友元函数和静态成员,如果父类只有有参数的构造函数,那么我们需要通过子类的构造函数的初始化列表进行初始化,这就意味着我们在调用子类构造函数的时候会率先调用父类的构造函数,但是我们在调用子类的析构函数的时候,会最后调用父类的析构函数,然后就是父类的友元函数和静态成员无法被继承,也就是说父亲的朋友并不是儿子的朋友,静态成员被父类和子类共享,示例代码如下

#include "stdafx.h"
#include<iostream>
#include<typeinfo>
using namespace std;

class A {
public:
A(int aa):a(aa){
cout << "这是A的构造函数" << endl;
}

~A()
{
cout << "这是A类的析构函数" << endl;
}
void show() {
cout << "this is a A class" << endl;
}
private:
int a;
};

class B :public A {
public:
B():A(13){
cout << "这是B的构造函数" << endl;
}
~B()
{
cout << "这是B类的析构函数" << endl;
}
void show() {;
cout << "this is a B class" << endl;
}
};

int main() {
B b;
b.show();
return 0;
}
//下面是运行结果
这是A的构造函数
这是B的构造函数
this is a B class
这是B类的析构函数
这是A类的析构函数

注意点3:继承中的覆盖问题,这个问题是指,如果在继承过程中,父类和子类中存在函数名称相同的函数,子类会对父类进行覆盖,这里并不是重载的关系

#include "stdafx.h"
#include<iostream>
using namespace std;

class A {
public:
A(int aa):a(aa){
cout << "这是A的构造函数" << endl;
}
void show(int b) {
cout << "this is a A class" << endl;
}
private:
int a;
};

class B :public A {
public:
B():A(13){
cout << "这是B的构造函数" << endl;
}

void show() {;
cout << "this is a B class" << endl;
}
};

int main() {
B b;
/*这里如果是重载关系的话,如果我们将b.show()中传入参数的话应该会调用A类的show方法
但是实际编译器会报错,因为这里进行的是覆盖并不是重载,如果我们想调用父类的show方法
只能通过类名作用域进行*/
b.show();
b.A::show(12);
return 0;
}

2.多态,通过父类指针去调用子类函数

注意点1:我们让一个父类指针指向子类空间,这是调用不到子类的方法的,我们可以这样理解,虽然我们的空间是按照子类去申请的,但是我们的指针类型仍然是父类的,所以我们这时候调用方法会去按照父类的规则去调用

#include "stdafx.h"
#include<iostream>
using namespace std;

class A {
public:

void show() {
cout << "this is a A class" << endl;
}
};

class B :public A {
public:
void show() {
cout << "this is a B class" << endl;
}

};

int main() {
A *a = new B;
a->show();
return 0;
}
//结果一定是输出 this is a A class

注意点2:为了能让父类指针调用子类函数,实现多态即通过泛型编程的思想进行, 通过让程序的运行呈现出多种状态,这里引入虚函数的概念,我们在A类的show方法前加上关键字virtual ,令其变成virtual void show(),这时候上面的程序的结果就会变成this is a B class,这是因为非虚函数在编译时便完成解析,根据指针的类型进行调用,如果是虚函数的话,则根据实际指针指向的空间进行调用,此操作在运行时进行。

#include "stdafx.h"
#include<iostream>
using namespace std;

class A {
public:
virtual void show() {
cout << "this is a A class" << endl;
}
};

class B :public A {
public:
void show() {
cout << "this is a B class" << endl;
}

void show_n() {
cout << "this is a b class" << endl;
}

};

int main() {
A *a = new B;
a->show();
return 0;
}
//输出 this is a B class
//这里无法通过a去调用show_n()方法,因为本质上a的指针类型仍然是A类

注意点3:父类和子类的函数的名字,参数返回值类型相同,如果父类是虚函数则叫做重写,如果返回值类型不同,则不叫重写,会报错。

#include "stdafx.h"
#include<iostream>
using namespace std;

class A {
public:
virtual void show() {
cout << "this is a A class" << endl;
}
};

class B :public A {
public:
void show(int a) {
cout << "this is a B class" << endl;
}

};

int main() {
A *a = new B;
a->show();
return 0;
}
//输出 this is a A class
//因为在运行时,我们发现B中的show方法是带参数的,所以只能调用的是A类中的show方法

#include "stdafx.h"
#include<iostream>
using namespace std;

class A {
public:
virtual void show(int a) {
cout << "this is a A class" << endl;
}
};

class B :public A {
public:
void show() {
cout << "this is a B class" << endl;
}

};

int main() {
A *a = new B;
a->show(12);
return 0;
}
/*
输出 this is a A class
分析 就像刚才提到的A.show(int a)是一个虚函数,所以需要在运行时在确定具体调用A类还是B类中的方法
然后实际运行时,编译器发现B中的show方法是无参的,不满足重写的要求,所以调用的是A中的方法
*/

#include "stdafx.h"
#include<iostream>
using namespace std;

class A {
public:
virtual void show(int a) {
cout << "this is a A class" << endl;
}
};

class B :public A {
public:
void show(int a) {
cout << "this is a B class" << endl;
}

};

int main() {
A *a = new B;
a->show(12);
return 0;
}
/*
输出 this is a B class
分析 明面上,我们通过父类指针去调用show方法,但是由于A中的show方法是虚函数,所以在运行时才能
决定到底调用哪一个类中的show方法,然后运行时编译器发现,B中的show方法完全符合重写的要求
所以会在运行时调用B中的show方法
*/

4.虚函数不能是构造函数,不能是内联函数 ,虚只针对于函数,不针对与变量

5. 我们如何通过父类指针去调用子类的函数呢,其实是通过虚函数表来实现的,那么什么是虚函数表呢?顾名思义,虚函数表就是用来存储虚函数地址的一个列表,在编译父类的时候会将虚函数的地址存入虚函数表中,然后当创建对象时,如果看到子类中的函数满足重写的要求,那么会用子类函数的地址去替换虚函数表中的地址,那么虚函数表中存放的就成了子类函数的地址。然后当我们用父类指针去调用show方法的时候,就会去虚函数表中取到show方法的地址,然后去执行。

3. typeid关键字

 

#include "stdafx.h"
#include<iostream>
#include<typeinfo>
using namespace std;

class A {
public:
virtual void show(int a) {
cout << "this is a A class" << endl;
}
};

class B :public A {
public:
void show(int a) {
cout << "this is a B class" << endl;
}

void showw(int a) {
cout << "this is a B class" << endl;
}
};

int main() {
A *a = new B;
a->show(12);
//cout << typeid(*a).name();
return 0;
}
/*
总结: 父类指针只能调用自己空间的成员(a是调用不到子类showw方法的),一个指针如何去使用空间是由类型决定,a指针的类型是A,所以只能访问A类中的成员,那么为什么能访问B中的show方法呢,是因为存在虚函数这种机制,通过虚函数表进行函数地址的一个覆盖,所以可以访问子类中的方法
*/