1. 继承
1.1继承的作用(重点)
1.代码复用,2.扩展类的功能
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//继承的作用1.代码复用。2.扩展类的功能
class Father
{
public:
void func1()
{
cout << "约小姐姐" << endl;
}
void func2()
{
cout << "有钱" << endl;
}
};
class Father01
{
public:
void func1()
{
cout << "约小姐姐" << endl;
}
void func2()
{
cout << "有钱" << endl;
}
};
class Son :public Father
{
public:
void func3()
{
cout << "有才" << endl;
}
};
class Son2 :public Father
{
public:
void func3()
{
cout << "有才" << endl;
}
};
void test()
{
Son s;
s.func1();
s.func2();
s.func3();
}
int main()
{
test();
system("pause");
return EXIT_SUCCESS;
}
1.2 继承方式(重点)
1.继承方式,公有继承,私有继承,保护继承
2.子类对从父类继承过来的成员的权限
1.公有继承
1.父类的公有属性成员,到子类还是公有
2.父类的私有属性成员,到子类不能访问
3.父类的保护属性成员,到子类还是保护
2.保护继承
1.父类的公有属性成员,到子类是保护
2.父类的私有属性成员,到子类不能访问
3.父类的保护属性成员,到子类还是保护
3.私有继承
1.父类的公有属性成员,到子类还是私有
2.父类的私有属性成员,到子类不能访问
3.父类的保护属性成员,到子类还是私有
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Father
{
public:
int a;
private:
int b;
protected:
int c;
};
class Son1 :public Father
{
public:
void func()
{
a;
//b;err
c;
}
/*
公有继承:
1.父类的公有属性成员,到子类还是公有
2.父类的私有属性成员,到子类不能访问
3.父类的保护属性成员,到子类还是保护
*/
};
void test01()
{
Son1 s1;
s1.a;
//s1.b;err
//s1.c;err
}
//保护继承
class Son2 :protected Father
{
public:
void func()
{
a;
//b;err
c;
/*
保护继承
1.父类的公有属性成员,到子类是保护
2.父类的私有属性成员,到子类不能访问
3.父类的保护属性成员,到子类还是保护
*/
}
};
void test02()
{
Son2 s2;
//s2.a;err
//s2.b;err
//s2.c;
}
class Son3 :private Father
{
public:
void func()
{
a;
//b;err
c;
/*
私有继承
1.父类的公有属性成员,到子类是私有
2.父类的私有属性成员,到子类不能访问
3.父类的保护属性成员,到子类还是私有
*/
}
};
class Son33 :public Son3
{
public:
void func()
{
//a;err
//c;err
}
};
void test03()
{
Son3 s3;
//s3.a;err
//s3.b;err
//s3.c;err
}
int main()
{
system("pause");
return EXIT_SUCCESS;
}
1.3 继承中的构造与析构(重点)
构造与析构的顺序
- 子类对象在创建时会首先调用父类的构造函数
- 父类构造函数执行完毕后,才会调用子类的构造函数
- 当父类构造函数有参数时,需要在子类初始化列表(参数列表)中显示调用父类构造函数
- 析构函数调用顺序和构造函数相反
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Father
{
public:
Father()
{
cout << "Father构造函数" << endl;
}
~Father()
{
cout << "Father析构函数" << endl;
}
};
class Son :public Father
{
public:
Son()
{
cout << "Son构造函数" << endl;
}
~Son()
{
cout << "Son析构函数" << endl;
}
};
//先调用父类的构造函数,再调用本身的构造函数,析构函数调用顺序反之
void test01()
{
Son s;
}
class A
{
public:
A()
{
cout << "A 的构造" << endl;
}
~A()
{
cout << "A 的析构" << endl;
}
public:
Son s;
};
class B
{
public:
B()
{
cout << "B 的构造" << endl;
}
~B()
{
cout << "B 的析构" << endl;
}
};
class C
{
public:
C()
{
cout << "C 的构造" << endl;
}
~C()
{
cout << "C 的析构" << endl;
}
};
class D:public A
{
public:
D()
{
cout << "D 的构造" << endl;
}
~D()
{
cout << "D 的析构" << endl;
}
public:
B b;
C c;
};
void test02()
{
D d;
}
int main()
{
test02();
system("pause");
return EXIT_SUCCESS;
}
1.4 继承中同名成员的处理方法(了解)
继承中同名函数的加载方法:
- 当子类成员和父类成员同名时,子类依然从父类继承同名成员
- 如果子类有成员和父类同名,子类访问其成员默认访问子类的成员(本作用域,就近原则)
- 在子类通过作用域::进行同名成员区分(在派生类中使用基类的同名成员,显示使用类名限定符)
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Father
{
public:
Father()
{
a = 10;
}
void func()
{
cout << "Father func" << endl;
}
void func(int a)
{
cout << "Father func(int a)" << endl;
}
void func(int a,int b)
{
cout << "Father func(int a,int b)" << endl;
}
public:
int a;
};
class Son :public Father
{
public:
Son()
{
a = 20;
}
void func()
{
cout << "Son func" << endl;
}
public:
int a;
};
//当子类和父类有同名成员时,子类的同名成员会隐藏父类的同名成员
void test()
{
Son s;
cout << s.a << endl;
cout << sizeof(Son) << endl;//8
Father f;
s.Father::a = 200;
//通过父类名加作用域来访问
cout<<s.Father::a << endl;
cout << f.a << endl;
}
//当子类有和父类同名函数时,父类的所有函数重载都会被隐藏
void test02()
{
Son s;
s.func();
//s.func(10);err
//s.func(10, 20);err
//通过作用域来访问隐藏的父类函数
s.Father::func(10);
s.Father::func(10, 20);
}
int main()
{
test();
system("pause");
return EXIT_SUCCESS;
}
1.5 继承中的静态成员特性(了解)
静态成员函数和非静态成员函数的共同点:
1.静态成员可以被继承
2.继承中的静态成员变量一样会被同名的子类成员变量隐藏
3.继承中的静态成员函数中,当子类有和父类同名静态函数时,父类的所有重载静态函数都会被隐藏
4.改变从基类继承过来的静态函数的某个特征,返回值或者参数个数,将会隐藏基类重载的函数
5.静态成员函数不能是虚函数
6.从父类继承过来的静态成员变量是父类的静态成员变量
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Father0
{
public:
static int mA;
};
class Father :public Father0
{
public:
static void func()
{
cout << "Father func()" << endl;
}
static void func(int a)
{
cout << "Father func(int a)" << endl;
}
static void func(int a, int b)
{
cout << "Father func(int a,int b)" << endl;
}
public:
static int mA;
};
int Father::mA = 10;
class Son :public Father
{
public:
/*static void func()
{
cout << "Son func()" << endl;
}*/
//static int func()
//{
// cout << "int func" << endl;
// return 0;
//}
public:
static int mA;
};
int Son::mA = 20;
void test01()
{
Son s;
cout << s.mA << endl;
s.Father::mA = 200;
cout << Father::mA << endl;
cout << &(s.Father::mA) << endl;
cout << &(Father::mA) << endl;
s.func();
s.func(10);
s.func(10, 20);
}
int main()
{
test01();
system("pause");
return EXIT_SUCCESS;
}
1.6 非自动继承的函数(了解)
构造,析构,赋值函数都不能被继承
1.7 多继承(了解)
1.多继承是一个类有两个以上的父类
2.多继承的问题是,当父类中有同名成员时,子类中会产生二义性问题
1.8 菱形继承(重点)
1.虚基类
被虚继承的基类叫虚基类
2.菱形继承的问题
两个父类中有祖类中的数据,然后子类会继承两个父类的数据,会产生二义性问题
3.虚继承
父类虚继承祖类,用virtual关键字
4.虚继承的原理
1.编译给类添加了一个指针,指针指向类似于表的组织,该表记录了该指针距离变量的偏移量
2.当子类多继承两个父类,那么只有一份成员变量,然后有两个指针,只有一份成员变量,所以不会产生二义性
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
//爷爷
//被虚继承的基类叫虚基类
class Animal
{
public:
Animal()
{
}
public:
};
//用虚继承的方法解决菱形继承中的二义性问题
//虚继承
class Sheep : virtual public Animal
{
};
//虚继承
class Camel :virtual public Animal
{
/*
class Camel size(8):
+---
0 | {vbptr}
+---
+--- (virtual base Animal)
4 | mA
+---
Camel::$vbtable@:
0 | 0
1 | 4 (Cameld(Camel+0)Animal)
*/
};
class SheepCamel :public Sheep, public Camel
{
public:
};
void test()
{
Camel c;
cout << sizeof(c) << endl;
}
void test02()
{
Sheep s;
//1.&s;
//2.(int*)&s;强转为int*类型
//3.*(int*)&s;//获取指针中的地址
//4.(int *)*(int*)&s;//指向表的首地址
//5.(int *)*(int*)&s+1;//指向了表存储偏移量的地址
//6.*((int *)*(int*)&s+1)//这就是偏移量
cout << *((int*)*(int*)&s + 1) << endl;
//1.&s
//2.(char*)&s;
//3.(char*)&s+*((int *)*(int*)&s + 1)
//4.把类型转换为Animal指针类型
//cout << ((Animal*)((char*)&s + *((int *)*(int*)&s + 1)))->mA << endl;
}
class A
{
public:
int a;
};
class B :public A
{
public:
};
class C :public B
{
};
void test03()
{
C c;
c.a;
}
int main()
{
test();
system("pause");
return EXIT_SUCCESS;
}
1.9 动态联编和静态联编(重点难点)
1.静态联编
编译器会根据函数调用的对象类型,在编译阶段就确定函数的调用地址,这就是静态联编(早绑定)
2.虚函数
在普通成员函数前面加virtual,该函数变为虚函数,是告诉编译器这个函数要晚绑定
3.动态联编
在运行阶段才确定调用哪个函数(晚绑定),
4.动态编译的作用,可以晚绑定函数调用地址,这样可以扩展功能,不修改前面的代码的基础上进行项目的扩充
5.类型转换问题
1.子类转换成父类(向上转换):编译器认为指针的寻址范围缩小了,所以是安全的
2.父类转换成子类(向下转换);编译器认为指针的寻址范围扩大了,不安全
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Animal
{
public:
//虚函数
virtual void speak()
{
cout << "Animal speak " << endl;
}
};
class Dog :public Animal
{
public:
void speak()
{
cout << "Dog speak " << endl;
}
};
class Dog1 :public Animal
{
public:
void speak()
{
cout << "Dog1 speak " << endl;
}
};
class Dog2 :public Animal
{
public:
void speak()
{
cout << "增加技能 " << endl;
}
};
//业务层
void doLogin(Animal *animal)//Animal *animal=dog;
{
animal->speak();
}
void test01()
{
Animal an;
an.speak();//在编译阶段就确定了调用speak这个函数
}
void test02()
{
Dog *dog = new Dog;
doLogin(dog);
Dog1 *dog1 = new Dog1;
doLogin(dog1);
Dog2 *dog2 = new Dog2;
doLogin(dog2);
}
int main()
{
test02();
system("pause");
return EXIT_SUCCESS;
}