继承与派生
类的继承与派生
继承的优点和缺点:
- 代码共享,减少创建类的工作量,每一个子类都有父类的方法和属性
- 提高代码的重用性,减少代码的冗杂程度
- 提高代码的可拓展性,调用别人的接口,加继承父类实现
- 继承是侵入性的,子类拥有父类的全部属性和方法
- 增强了耦合性,父类常量、变量、方法被修改时,子类可能需要重构
继承代码示例
class Student
{
public:
Student(int id, string name)
{
this->id = id;
this->name = name;
}
void printS()
{
cout << "id=" << this->id << " name=" << this->name << endl;
}
private:
int id;
string name;
};
class Student2
{
public:
Student2(int id, string name, int score)
{
this->id = id;
this->name = name;
this->score = score;
}
void printS()
{
cout << "id = "
<< this->id
<< ",name="
<< this->name
<< endl;
cout << "score= " << this->score << endl;
}
private:
int id;
string name;
int score;
};
//Student3继承于Student,子类继承了父类的成员和公有的函数
class Student3 :public Student
{
public:
Student3(int id, string name, int score) :Student(id, name)
{
this->score = score;
}
void printS()
{
Student::printS();
cout << "score = " << this->score << endl;
}
private:
int score;
};
//类Student3通过继承Student实现了类Student2的功能
派生类的组成,基类+新增成员
继承的方式
- 只要是父类中的private成员,不管什么继承方式,子类都无法继承,但可以通过public来间接访问,比如get(),构造函数什么的
- 如果是public继承,子类的访问控制权限保持不变
- 如果是proteceed继承,子类中除了private成员,其余在子类中都是protected
- 子类可以直接访问父类的public成员
- 子类内部可以访问父类protected成员,但类外不能访问
三条原则:
- 看调用的成员变量是在类的外部还是在类的内部
- 看当前变量在子类中的访问控制权限
- 看子类的继承方式
public | protected | private | |
---|---|---|---|
公有继承(public) | public | protected | 不可见 |
保护继承(protected) | protected | protected | 不可见 |
私有继承(private) | private | private | 不可见 |
代码示例:
class Parent
{
public:
int pub;//类的内部和外部都可以访问
protected:
int pro;//类的内部可以访问,类的外部不可以访问
private:
int pri;//类的内部可以访问,类的外部不可以访问
};
class Child :public Parent
{
public:
void func()
{
cout << pub << endl;
}
};
//保护继承
class Child2 :protected Parent
{
public:
void func2()
{
pub;//通过protected继承,能够在类的内部访问
//pub在类的内部可以访问,类的外部不可以访问,子类可以访问
//pub就是protected成员
pro;//pro跟pub是一样的性质,都是protected
}
};
class Sub_child2 :public Child2
{
public:
void sub_func2()
{
pub;
pro;
}
};
//继承Child类
class SuChild :public Child
{
void func2()
{
}
};
class Child3 :private Parent
{
public:
void func3
{
pub;//类的内部可以访问,类的外部不能访问
//子类不能访问
pro;//跟pub一样
}
};
class Sub_Child3 :public Child3
{
public:
void sun_fun3()
{
}
};
继承关系具体代码举例
#include<iostream>
using namespace std;
class A
{
private:
int a;
protected:
int b;
public:
int c;
A()
{
a = 0;
b = 0;
c = 0;
}
void set(int a, int b, int c)
{
this->a = a;
this->b = b;
this->c = c;
}
};
class B :public A
{
public:
void print()
{
//cout << "a= " << a;//a是父类的私有成员,访问不了
cout << "b= " << b;//b在类的内部可以访问
cout << "c= " << c << endl;//类的内部和外部都可以访问
}
};
class C :protected A
{
public:
void print()
{
//cout << "a= " << a;//a是父类的私有成员,访问不了
cout << "b= " << b;//b在类的内部可以访问,类的外部不能访问
cout << "c= " << c << endl;//类的内部和外部都可以访问
}
};
class D :private A
{
public:
void print()
{
//cout << "a= " << a;//a是父类的私有成员,访问不了
cout << "b= " << b;//私有继承b在类的内部可以访问,外部不可以访问
cout << "c= " << c << endl;//类的内部可以访问,外部不可以访问
}
};
int main()
{
A aa;
B bb;
C cc;
D dd;
aa.c = 100;
bb.c = 100;
//cc.c = 100;//不可访问
//dd.c = 100;//不可访问
aa.set(1, 2, 3);
bb.set(1, 2, 3);
//cc.set(1, 2, 3);//不能访问
//dd.set(1, 2, 3);//不能访问
bb.print();
cc.print();
dd.print();
return 0;
}
类的兼容性原则
- 子类对象可以当做父类对象使用
- 子类对象可以直接赋值给父类对象
- 子类对象可以直接初始化父类对象
- 父类指针可以直接指向子类对象
- 父类引用可以直接引用子类对象
练习代码:
#include<iostream>
using namespace std;
class Parent
{
public:
int a;
//int c;
void printP()
{
cout << "a" << this->a << endl;
}
};
class Child :public Parent
{
public:
void printC()
{
cout << "b=" << this->a;
}
int b;
};
void myPrint(Parent* pp)
{
pp->printP;
}
int main(void)
{
Parent* pp=NULL;
Child* cp=NULL;
Parent p;
Child c;
pp = &c;//c的内存布局能够满足父类指针的全部需求
//可以用一个子类的对象地址给父类指针赋值
pp->a;
pp->printP(&c);
return 0;
}
子类的构造与析构
- 子类构造函数会默认调用父类无参构造
- 在子类构造函数之前会先调用父类的构造函数
- 如果子类要调用父类的有参构造,需要给父类有参构造函数传值
- 析构函数会先调用子类的析构函数然后才调用父类的析构函数
代码示例
#include<iostream>
using namespace std;
class Parent
{
public:
Parent()
{
a = 0;
cout << "parent()" << endl;
}
Parent(int a)
{
this->a = a;
cout << "parent(int)" << endl;
}
~Parent()
{
cout << "Parent析构" << endl;
}
int a;
};
class Child :public Parent
{
public:
//在调用子类,默认调用父类构造函数
Child(int a,int b):Parent(a)
{
cout << "Child(int,int)..." << endl;
this->a = a;
this->b = b;
}
void printC()
{
cout << "b=" << b << endl;
}
~Child()
{
cout << "Child析构" << endl;
}
int b;
};
int main()
{
Child c(10,20);
c.printC();
//c.~Child();
return 0;
}
子类与父类的成员重名
- 父类和子类即使成员变量名相同,但由于内存地址不同,不影响
- 父类成员变量名通过::访问
- 传参过程是先在子类构造函数建临时变量,通过临时变量传参
代码示例:
#include<iostream>
using namespace std;
class Parent
{
public:
int a;
Parent(int a)
{
this->a = a;
}
};
class Child :public Parent
{
public:
int a;
Child(int p_a,int c_a):Parent(c_a)//Parent(p_a)
{
this->a = p_a;//this->a = c_a;
}
void print()
{
cout << Parent::a << endl;
cout << this->a << endl;//子类的A
}
};
int main()
{
Child c(10, 100);
c.print();
return 0;
}
继承中的static
- static在类内声明,在类外初始化
- static归类,所有成员共享
- static在继承关系中,同样也别被子类共享
多继承与虚继承
-
关键字virtual,虚继承,在发生多继承的时候,子类重复的只会有一个继承
-
多继承即一个子类继承了多个父类,仅C++有
class 子类:public 父类,public 父类....
-
虚继承重复的部分只有一块内存
代码示例:
#include<iostream>
using namespace std;
//定义家具类
class Furniture
{
public:
int m;//材质
};
//继承父类虚继承,防止子类发生不明确现象
class Bed:virtual public Furniture
{
public:
void sleep()
{
cout << "go to bed" << endl;
}
};
class Sofa:virtual public Furniture
{
public:
void sit()
{
cout << "relax on the sofa" << endl;
}
};
class SofaBed:public Bed,public Sofa
{
public:
void SleeAndSit()
{
sleep();
sit();
}
};
int main()
{
Bed b;
b.sleep();
Sofa s;
s.sit();
cout << "-------------" << endl;
SofaBed sb;
sb.SleeAndSit();
sb.m = 100;//如果不加vitural不明确只能够通过::访问
system("pause");
return 0;
}