继承(2),咕咕咕!
(1)继承与友元
友元关系不能继承,基类友元不能访问派生类私有和保护成员。
class student;
class person
{
public:
friend void display(const person& p,const student& s);
protected:
string name;
};class student:public person
{
protected:
int stuid;
};void display(const person& p,const student& s)
{
cout<<p.name<<endl;
cout<<s.stuid<<endl;
}int main()
{
person p;
student s;
//编译报错,student::stuid无法访问protected成员
//把display函数也变成student的友元函数就行了
display(p,s);
return 0;
}
2.继承与静态成员
基类定义了static静态成员,则整个继承体系内只有一个这样的成员,无论派生出多少个派生类,都只有一个static成员实例。
class person
{
public:
string name;
static int count;
};int person::count=0;class student:public person
{
protected:
int stuid;
};int main()
{
person p;
student s;
//这里可以看到非静态成员name的地址不一样
//派生类继承下来,父派生类对象各有一份
cout<<&p.name<<endl;
cout<<&s.name<<endl;
//这里的运行结果看到静态成员count的地址一样
//派生类和基类共用同一份静态成员
cout<<&p.count<<endl;
cout<<&s.count<<endl;
//公有的情况下,父派生类指定类域都可以访问静态成员
cout<<person::count<<endl;
cout<<student::count<<endl;
return 0;
}
3.多继承以及菱形继承问题
单继承:一个派生类只有一个直接基类
多继承:一个派生类有两个或以上直接基类。多继承对象在内存中的模型是,先继承的基类在前面,后继承的基类在后面,派生类的成员在最后。
菱形继承:菱形继承是多继承的一种特殊情况。菱形继承的问题是有数据冗余和二义性。在assistant的对象中person成员会有2份,支持多继承就一定会有菱形继承,实践中不建议。

class person
{
public:
string name;
};
class student:public person
{
protected:
int num;
};
class teacher:public person
{
protected:
int id;
};
class assistant:public student,public teacher
{
protected:
string majorcourse;
};
int main()
{
assisiatnt a;
a.name="gugu";//编译报错,对name的访问不明确,要显示指定基类的成员,但是数据冗余未解决a.studnet::name="1";
a.teahcer::name="0";
}
4.虚继承
虚继承的底层实现比较复杂,性能也会有一定损失。
class person
{
public:
string name;
};
class student:virtual public person
{
protected:
int num;
};
class teacher:virtual public person
{
protected:
int id;
};
class assistant:public student,public teacher
{
protected:
string majorcourse;
};
int main()
{
//使用虚继承解决数据冗余和二义性
assisiatnt a;
a.name="gugu";
}
虽然有时候虚继承可以解决菱形继承的问题,但是还是不建议用菱形继承,因为虚继承来解决也会有新的问题。
class person
{
public:
person(const char* name):name(name){}
string name;
};
class student:virtual public person
{
public:
student(const char* name,int num):person(name),num(num){}
protected:
int num;
};
class teacher:virtual public person
{
public:
teacher(const char* name,int id):person(name),id(id){}
protected:
int id;
};
//不建议菱形继承的例子,因为会变复杂
//在虚继承中,最顶层的基类(这里是person)的构造函数不由中间派生类(student/teacher)调用,
//而是由最终派生类(assistant)直接调用。
class assistant:public student,public teacher
{
public:
assistant(const char*name1,const char* name2,const char* name3)
:person(name3),student(name1,1),teacher(name2,2)
{}
protected:
string majorcourse;
};int main()
{
assistant a("gugu","gugugu","gugugugu");//最后是gugugu,只初始化了第一个
return 0;
}
多继承还有指针偏移的现象。
class base1{public:int b1;};
class base2{public:int b2;};
class derive:public base1,public base2{public:int d;};
int main()
{
derive d;
base1* p1=&d;
base2* p2=&d;
derive* p3=&d;
}p1=p3!=p2 p1/p3->----------------| ||--------------| p2->----------------

IO库中的菱形虚拟继承
template<class charT,class Traits=std::char_traits<charT>>
class basic_ostream:virtual public std::basic_ios<charT,Traits>{};
template<class charT,class Traits=std::char_traits<charT>>
class basic_istream:virtual public std:basic_ios<charT,Traits>{};
5.继承和组合
public是一种is-a关系,每个派生类对象都是一个基类对象
组合是一种has-a关系,假设B组合了A,每个B对象都有一个A对象
继承允许根据基类的定义来定义派生类的实现。这种通过生成派生类的复用称为白箱复用。在继承方式中,基类的内部细节对派生类可见。继承一定程度破坏了基类的封装,基类的改变对派生类有很大的影响。派生类和基类的依赖关系很强,耦合性高。
对象组合是类继承外另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象有良好定义的接口,称为黑箱复用,对象的内部不可见,组合之间没有强伊依赖关系,耦合性低,优先使用对象组合有助于保持类的封装性。
类之间的关系就适合is-a就用继承,要实现多态就用继承。类之间的关系既适用于is-a又适用于has-a优先使用组合。
//has-a关系
class tire
{
protected:
string brand;
size_t size'
};
class car
{
protected:
string colour;
string num;
tire t1;
tire t2;
tire t3;
tire t4;
};
//is-a关系
class gugu:public car
{
public:
void drive(){cout<<"gugu"<<endl;}
};
