2.访问限定符 public (公有) protected (保护) private (私有) 3.三种继承关系下基类成员的派生类访问关系变化: public继承:基类的非私有成员在子类里的访问属性不变都不变 protected继承:基类的非私有成员都成为子类的保护成员 private继承:基类中的非私有成员都成为子类的私有成员 无论哪种继承方式,基类的私有成员都是不可见的。 4.总结 (1)基类的私有成员在派生类中是不能被访问的,如果一些基类成员不想被基类对象直接访问,但需要在派生类中能访问,就定义为保护成员,可以看出保护成员限定符是因继承才出现的。 (2)public继承是一个接口继承,保持is-a的关系原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。 (3)protected/private继承是一个实现继承,基类部分成员并未完全成为子类接口的一部分,是has-a的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。 (4)不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员。 (5)使用关键字class时默认继承方式是private,使用struct时默认继承方式是pubic,最好写出继承方式。 (6)在实际运用中一般都是使用public继承,极少场景下才会使用private/protected继承。 5.继承与转换—赋值兼容规则—public继承 (1)子类对象可以赋值给父类对象,但父类对象不可以赋值给子类对象 (2)父类的指针/引用可以指向子类对象,子类的指针/引用不可以指向父类对象(可以通过强制类型转换完成)
#include <iostream> using namespace std; class A //基类 父类 { public: int _a; }; class B :public A//派生类 子类 (继承了父类public A) { public: int _b; }; //运行结果解释见下面图一 int main() { A aa; aa._a = 1; B bb; bb._a = 2; bb._b = 3; aa = bb; system("pause"); return 0; } //运行结果及解释见图二 int main() { A aa; aa._a = 1; B bb; bb._a = 2; bb._b = 3; //aa = bb;//子类对象可以给父类对象 //A* p = &bb; //父类对象指针可以指向子类对象 A& r = bb;//父类对象可以是子类对象的别名 //指针引用语法在实际运行底层都创建了指针变量 r._a = 10; aa = bb; system("pause"); return 0; }运行结果图一: 图二: 6.子类对象可以赋给父类对象(切割/切片) 7.隐藏 (重定义) 继承体系中的作用域:在继承体系中基类和派生类都有独立的作用域。 子类、父类同名成员(函数/变量),子类会隐藏父类同名成员。 子类和父类中有同名成员,子类将屏蔽父类对成员的直接访问。(在子类成员函数中,可以使用基类::基类成员 访问) —隐藏 —重定义; 注意在实际中继承体系里面最好不要定义同名的成员 。
#include <iostream> using namespace std; class AA { public: int f(int a) { cout<<"AA::f()"<<endl; return 0; } int a; }; class BB : public AA { public: void f(double a) { cout<<"BB::f()"<<endl; } int a; int b; }; // 隐藏 int main() { BB bb; bb.a = 10; bb.AA::a = 20; bb.f(1); //bb.AA::f(1); return 0; }8.类的六个默认成员函数:构造函数,析构函数,拷贝构造函数,赋值运算符重载函数,取地址操作符重载,const修饰的取地址操作符重载 派生类的默认成员函数 在继承关系里面,在派生类中如果没有显示定义这六个默认成员函数,编译系统则会默认合成这六个默认成员函数。
#include <iostream> #include <string> using namespace std; class Person { public: Person(char* name = "小黑")//类对象成员初始化 :_name(name) { cout << "Person()" << endl; } Person(const Person& p)//拷贝构造函数 :_name(p._name) { cout << "Person(const Person& p)" << endl; } Person& operator=(const Person& p)// 赋值操作符重载 { cout << "Person& operator=(const Person& p)" << endl; if (this != &p) { _name = p._name; } return *this; } ~Person()//析构函数 { cout << "~Person()" << endl; } protected: string _name; }; class Student : public Person { public: Student(char* name, int num)//子类构造函数 :Person(name)//直接复用父类构造函数 , _num(num) { cout << "Student()" << endl; } Student(const Student& s)//拷贝构造函数 :Person(s) , _num(s._num) { cout << "Student(const Student& s)" << endl; } Student& operator=(const Student& s)//赋值运算符重载 { if (this != &s) { Person::operator=(s); _num = s._num; } cout << "Student& operator=(const Student& s)" << endl; return *this; } ~Student()//析构函数 { cout << "~Student()" << endl; } protected: int _num; // 学号 }; void test1() { Person p; Student s("Mary", 17); } int main() { test1(); system("pause"); return 0; }好了,今天继承相关的知识就讲到这里,继承多态里面有很多知识点,有时容易混淆,慢慢地去理解,你会发现代码有时也是很好玩的,之后的相关知识我会继续分享的,Bye~
