类有一种继承的方式,很有趣。一般继承是公有继承。 继承的方式有三种:公有继承(public),保护继承(protected),私有继承(private)。
.publicprotectedprivate公有继承publicprotected不可见保护继承protectedprotected不可见私有继承privateprivate不可见public:类的内部可以访问。类的外部可以访问 protected:类的内部可以访问。类的外部不能访问 private:类的内部可以访问。类的外部不能访问
如何恰当的使用public,protected和private为成员声明访问级别? 1、需要被外界访问的成员直接设置为public 2、只能在当前类中访问的成员设置为private 3、只能在当前类和子类中访问的成员设置为protected,protected成 员的访问权限介于public和private之间。
子类与父类之间的关系: 1.子类对象可以当作父类对象使用。 2.子类对象可以直接赋值给父类对象。 3.子类对象可以直接初始化父类对象. 4.父类指针可以直接指向子类对象 5.父类引用可以直接引用子类对象
代码如下:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Parent { public: void printP() { cout << "a " << this->a << endl; } int a; }; class Child :public Parent { public: void printC() { cout << "b = " << this->b << endl; } int b; }; void myPrint(Parent *pp) { pp->printP(); } int main(void) { Parent p; //Child c = p; //p对象填充不满c对象空间, Child c; Parent p = c;//c 对象所占用的内存空间 >= p对象占用空间 能够填充满p对象所需要空间。 p = c; // c.printP(); //c 能够当做父类 p 来使用。 Parent *pp = NULL;//父类指针 Child *cp = NULL;//子类指针 Parent p;//父类对象 Child c; //子类对象 pp = &c;//c 内存布局能够满足父类指针的全部需求, 可以用一个儿子的对象地址给父类指针赋值。 myPrint(&p); myPrint(&c); return 0; }1.在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初 始化. 2.在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清 理.
继承和组合并存,构造和析构原则是声明呢? 答案是: 先构造父类,再构造成员变量、最后构造自己 先析构自己,在析构成员变量、最后析构父类
代码如下:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Parent { public: Parent() { cout << "Parent().." << endl; a = 0; } Parent(int a) { cout << "Parent(int)..." << endl; this->a = a; } ~Parent() { cout << "~Parent" << endl; } int a; }; class Child :public Parent { public: //在调用子类的构造函数时候,一定会调用父类的构造函数 // 父类先构造,子类后构造。 Child(int a, int b) :Parent(a) { cout << "Child(int, int)..." << endl; this->b = b; } void printC() { cout << "b = " << b << endl; } ~Child() { cout << "~Child()..." << endl; } int b; }; int main(void) { Child c(10, 20); c.printC(); return 0; }运行结果如下:
如果是父类中有一个成员,与子类中的变量重名,要怎么解决呢,看以下代码:
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; class Parent { public: Parent(int a) { this->a = a; } int a; }; class Child :public Parent { public: Child(int p_a, int c_a) :Parent(p_a) { this->a = c_a; } void print() { cout << Parent::a << endl;//在子类中使用父类的a,需要添加作用域。 cout << this->a << endl;//child's a } int a; }; int main(void) { Child c(10, 100); c.print(); return 0; }运行结果如下:
一个类同时继承两个类。
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则 在对该基类中声明的名字进行访问时,可能产生二义性 虚继承声明使用关键字 virtual
虚继承为了防止这种情况下,类C,继承类B的成员后,不清晰是从类B1中继承下来的,还是类B2中继承下来的。 下面看一个例子。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std; //家具类 class Furniture { public: int m; //材质 }; //将父亲类继承爷爷类 改成虚继承, 防止儿子在多继承我的时候,出现爷爷中的变量会拷贝多份。 class Bed :virtual public Furniture { public: void sleep() { cout << "在床上睡觉" << endl; } }; class Sofa :virtual public Furniture { public: void sit() { cout << "在沙发上休息" << endl; } }; //沙发床 class SofaBed :public Bed, public Sofa { public: void SleepAndSit() { sleep(); sit(); } }; int main(void) { Bed b; b.sleep(); Sofa s; s.sit(); cout << " ------ " << endl; SofaBed sb; sb.SleepAndSit(); sb.m = 100;//此时只有一个m //sb.Bed::m = 100; //sb.Sofa::m = 200; return 0; }运行结果如下:
多态发生的条件: 1.要有继承 2.要有虚函数重写 3.要有父类指针(父类引用)指向子类对象
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> using namespace std; //岳不群 class Yuebuqun { public: Yuebuqun( string kongfu) { this->kongfu = kongfu; } virtual void fight() //标识修饰一个成员方法是一个虚函数。 { cout << "岳不群" << "使出了" << kongfu << "打人" << endl; } void print() { } string kongfu; }; //林平之 继承了 岳不群 class Linpingzhi :public Yuebuqun { public: Linpingzhi(string kongfu) :Yuebuqun(kongfu) { } //如果说父类中有一个虚函数是fight( ), 子类如果去重写这个虚函数。 void fight() { cout << "林平之" << "使出了" << kongfu << "打人" << endl; } void print() { } }; class Linghuchong :public Yuebuqun { public: Linghuchong(string kongfu) :Yuebuqun(kongfu) { } void fight() { cout << "令狐冲 " << "使用了" << kongfu << endl; } }; //在全局提供一个打斗的方法 void fightPeople(Yuebuqun *hero)//Yuebuqun *hero = xiaopp; Yuebuqun *hero = xiaoyy; { cout << "调用打人的方法" << endl; hero->fight();//希望传递进来的如果是子类,调用子类的fight //如果传递进来的是父类, 调用父类的fight //这种行为就是 多态行为。 } //多态发生的三个必要条件: //1. 要有继承。 //2. 要有虚函数重写。 //3. 父类指针或引用 指向 子类对象。 int main(void) { Yuebuqun *xiaoyy = new Yuebuqun("葵花宝典"); //xiaoyy->fight(); Linpingzhi *xiaopp = new Linpingzhi("僻邪剑谱"); //xiaopp->fight(); Linghuchong *xiaoll = new Linghuchong("独孤九剑"); fightPeople(xiaoyy); fightPeople(xiaopp); fightPeople(xiaoll); //编译器默认做了一个安全的处理。 编译认为 不管传递时子类对象还是父类对象, //如果统一执行父类d方法 那么是一定可以被成功执行。 delete xiaoyy; delete xiaopp; delete xiaoll; return 0; }如果父类在堆上开辟空间。子类也在堆上开辟空间。 进行析构的时候,会先构造父类,在构造子类。再进行析构时,如果不是虚构造函数,子类无法析构掉,导致内存泄露。
#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class A { public: A() { p = new char[20]; strcpy(p,"obja"); printf("A()\n"); } virtual ~A() { delete[] p; printf("~A()\n"); } private: char *p; }; class B : public A { public: B() { p = new char[20]; strcpy(p, "objb"); printf("B()\n"); } ~B() { delete[] p; printf("~B()\n"); } private: char *p; }; class C : public B { public: C() { p = new char[20]; strcpy(p, "objc"); printf("C()\n"); } ~C() { delete[] p; printf("~C()\n"); } private: char *p; }; //通过⽗类指针 把 所有的⼦类对象的析构函数 都执⾏⼀遍 //通过⽗类指针 释放所有的⼦类资源 void howtodelete(A *base) { delete base; } int main() { C *myC = new C; //delete myC; //直接通过⼦类对象释放资源 不需要写virtual howtodelete(myC);//通过⽗类的指针调⽤释放⼦类的资源 return 0; }运行结果如下:
1.重载:一定是发生在同一个作用域下。 2.重定义:是发生在两个不同的类中,一个父类,一个子类。 (1)普通函数重定义 (2)虚函数重写