1️⃣ 继承的概念:继承是是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
2️⃣ 继承的定义:
class Person
{
public:
string _name;
string _sex;
int _age;
};
class Student :public Person
{
public:
int _No;
};
下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类
3️⃣基类和派生类对象赋值转化:
class Person
{
public:
string _name;
string _sex;
int _age;
};
class Student :public Person
{
public:
int _No;
};
int main()
{
Person p;
Student s;
s._name = “张三”;
s._age = 20;
s._sex = “男”;
p = s;
Person& rp = s;
Person* ptrp = &s;
cout << &s << endl;
cout << &rp << endl;
cout << ptrp << endl;
rp._age++;
ptrp->_age++;
return 0;
}
4️⃣继承中的作用域:
我们通过上图,发现父类和子类都要_num我们进行实现的时候用的是子类的_num解释了上面的第二点:子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问。我们要处理这个问题我们可以通过确定访问的方式进行指定访问。
我指定了它是访问父类。通过运行我们发现这个方法是可行的。
5️⃣.派生类的默认成员函数
5. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
6. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
7. 派生类的operator=必须要调用基类的operator=完成基类的复制。
8. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
9. 派生类对象初始化先调用基类构造再调派生类构造。
10. 派生类对象析构清理先调用派生类析构再调基类的析构。
class Person
{
public:
Person(const 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(const char name=" ",int num=0)
:_num(num)
,Person(name)
{
cout << “Student(const char* name=” “,int num=0)” << 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;
};
int main()
{
Student s1(“李四”, 1);
Student s2(s1);
Student s3(“王五”, 2);
s1 = s3;
return 0;
}
以上10点需要读者自己通过这部分代码进行探究。
6️⃣继承与友元
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
如上图对基类使用了友元,编译器报错完美解释了友元关系不能继承
7️⃣继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。
class Person
{
public:
Person()
{
++_count;
}
protected:
string _name;
public:
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
class Graduate :public Student
{
protected:
string _seminarCourse;
};
int main()
{
Student s1;
Student s2;
Student s3;
Graduate s4;
Person s;
cout << " 人数 :" << Person::_count << endl;
cout << " 人数 :" << Student::_count << endl;
cout << " 人数 :" << s4._count << endl;
cout << " 人数 :" << &Person::_count << endl;
cout << " 人数 :" << &Student::_count << endl;
cout << " 人数 :" << &s4._count << endl;
return 0;
}
我们通过该代码进行验证。发现static定义的_count的静态变量在类里面是唯一的。我们可以进行多次改变。
8️⃣指定访问
指定访问是为了解决复杂的菱形继承中的二义性。
什么是菱形继承?
菱形继承:是多继承的一种特殊情况。
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。
单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
lass Person
{
public:
string _name;
int _a[10000];
};
class Student : virtual public Person
{
protected:
int _num;
};
class Teachar :virtual public Person
{
protected:
int _id;
};
class Assistant :public Student, public Teachar
{
protected:
string _majorCourse;
};
以上就是指定访问的使用。
9️⃣ virtual的使用
虚拟继承的出现是为了解决菱形继承中数据冗余和二义性的问题。
虚拟继承写在继承方式的前面。
虚拟继承的使用,请百度。本人对虚拟继承的认知不足。