class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "peter"; // 姓名
int _age = 18; // 年龄
};
class Student : public Person
{
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Student s;
Teacher t;
s.Print();
t.Print();
return 0;
}
2.继承的定义
![](https://img-blog.csdnimg.cn/d9eab724d5a947c0af6b2ae4c28e46c8.png)
![](https://img-blog.csdnimg.cn/cf0b15e7f022422c8c335048781223f8.png)
继承基类成员访问方式的变化 :
三. 继承中作用域
// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆
class Person
{
protected :
string _name = "小李子"; // 姓名
int _num = 111; // 身份证号
};
class Student : public Person
{
public:
void Print()
{
cout<<" 姓名:"<<_name<< endl;
cout<<" 身份证号:"<<Person::_num<< endl;
cout<<" 学号:"<<_num<<endl;
cout<<" 学号:"<<Person::_num<<endl;
}
protected:
int _num = 999; // 学号
};
void Test()
{
Student s1;
s1.Print();
};
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B : public A
{
public:
void fun(int i)
{
A::fun();
cout << "func(int i)->" << i << endl;
}
};
int main(void)
{
B b;
b.fun(10);
return 0;
}
四.派生类的默认成员函数(在公有继承的前提下)
6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个成员函数是如何生成的呢?
测试代码:
class Person
{
public:
Person(const char* name = "peter")
: _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(const 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)
{
cout << "Student& operator= (const Student& s)" << endl;
if (this != &s)
{
Person::operator =(s);
_num = s._num;
}
return *this;
}
~Student()
{
cout << "~Student()" << endl;
}
protected:
int _num; //学号
};
int main(void)
{
Student s1("jack", 18);
Student s2(s1);
Student s3("rose", 17);
s1 = s3;
return 0;
}
从上例中我们可以得到一个结论,就是所谓派生类从基类中继承基类的东西,其实说白了,相当于是在派生类中弄了一个基类的子对象,对这个子对象的创建销毁操作还是要基类来干,属于是谁的娃谁来带!!!
五.继承与友元
友元关系不可以继承。
六.继承与静态成员
静态成员是可以被继承的,但是该静态成员的空间并没有发生改变,不会在派生类中拷贝一份。
七.多继承
1.单继承与多继承
单继承:
多继承:
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 ; // 主修课程
};
void Test ()
{
// 这样会有二义性无法明确知道访问的是哪一个
Assistant a ;
a._name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
}
解决方法:虚拟继承
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地 方去使用。
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 ; // 主修课程
};
void Test ()
{
Assistant a ;
a._name = "peter";
}
class A
{
public:
int _a;
};
// class B : public A
class B : virtual public A
{
public:
int _b;
};
// class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
![](https://img-blog.csdnimg.cn/6b5363d587a14bcf86afcfc3408f680c.png)
![](https://img-blog.csdnimg.cn/586c2fd6f6e343e59f11638a626ddf16.png)
![](https://img-blog.csdnimg.cn/e56364fc8e7842819782e37d1f206d1b.png)
一个例题:
此时我们说 p2==p3!=p1对吗?
多继承中在存储空间中先继承的在前面:
根据此图显然结果是正确的。
本质上继承和组合都是一种复用,但是继承是白盒复用,组合是黑盒复用。
因为继承是可以实打实的看到基类中的组成结构的。
但是组合只是在其他类中定义了一个类的对象,我们只是去使用这个对象,对象的内部结构我们是不知道的。