1、概念和定义
继承是一种类层次的复用,继承中分为基类(父类)和子类(派生类),父类的一些属性,子类同样可以拥有。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "zyd";
int _age = 21;
};
class Student : public Person
{
protected:
int _stuid;
};
class Teacher : public Person
{
protected:
int _jobid;
};
int main()
{
Student s;
s.Print();
return 0;
}
可以正常打印。此时s里面就有name, age,和未初始化是随机值的stuid变量。
继承有继承的方式,比如上面代码中的public继承
虽然基类的private成员派生类无法访问,但可以用基类中不是私有的函数来调用私有变量。
如果不写访问限定符,那就是私有继承。
2、基类和派生类的赋值转换
如果是公有继承,可以这样写
Student s;
Person p = s;
Person& rp = s;
不会发生类型转换.p = s,就是把子类当中父类的部分给赋值过去;而引用,则是子类当中父类那一部分的引用,所以rp可以修改,访问。如果是指针,就指向子类中父类那一部分。
但是现阶段父类对象不能赋值给子类对象。
赋值兼容、切割、切片
B b = d;
B* ptrb = &d;
把子类对象赋值给父类对象,把子类中父类那一部分给过去。
3、继承中的作用域
父子类可以有同名成员,不过真实数据由子类决定。想访问父类的内容就得显式一下
cout << Person::_age << endl;
cout << _age << endl;
s.Person::Print();
父子类用重名内容时,就出现了隐藏的现象。
4、派生类中的默认成员函数
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
{
protected:
int _num;
};
int main()
{
Student s;
return 0;
}
只是实例化,但是子类会自动调用父类的成员函数。
那子类自己写一个构造函数,调用自己的行不行
不行。即使显式调用_name也不行。父子类的规定就是父类的成员变量用父类的函数,子,可以这样改。
Student(const char* name, int num)
:Person(name)
,_num(num)
{}
如果在子类构造函数不写某一个变量的初始化也可以,只要父类能构造就行。
拷贝构造如果没写,那就调用父类的,写的话
Student(const Student& s)
:Person(s)
,_num(s._num)
{}
_num是子类变量。如果这样
Person p = s1;
会调用父类的拷贝构造。赋值重载
Student& operator=(const Student& s)
{
if (this != &s)
{
Person::operator=(s);
_num = s._num;
}
return &this;
}
所以可以看到,各分各的,有父类的那就调用父类函数,否则就调用的自己的。
现在的代码结果是这样
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)
{
if (this != &s)
{
Person::operator=(s);
_num = s._num;
}
cout << "Student(const Student= s)" << endl;
return *this;
}
protected:
int _num;
};
int main()
{
Student s1("asdasd", 21);
Student s2(s1);
Person p = s1;
s1 = s2;
return 0;
}
写上析构函数
~Student()
{
Person::~Person();
cout << "~Student()" << endl;
}
Student s1("asdasd", 21);
会发现多了一个Person。如果去掉Person::Peroson就正常了,析构函数不要显式调用,编译器会自动调用。
构造时会先构造父,析构时会先析构子。
5、继承与友元
友元不能继承
class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
int main()
{
Person p;
Student s;
Display(p, s);
return 0;
}
如果要访问,就得再定义一个友元。
6、继承与静态成员
静态变量也不能继承,但由于是静态区的,哪里都可以访问。
class Person
{
public:
Person() { ++_count; }
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()
{
Person p;
Student s;
cout << &(p._name) << endl;
cout << &(s._name) << endl;
cout << &(p._count) << endl;
cout << &(s._count) << endl;
}
7、复杂的菱形继承及菱形虚拟继承
单继承是一个子类只有一个直接父类。
多继承是一个子类有两个或以上父类。
菱形继承是多继承的一种特殊情况。
实际上,多继承变得更复杂了,会出现数据冗余和二义性(无法明确访问哪一个)的问题。
8、组合
虚继承写起来麻烦,需要看内存各个变量的地址,所以就不写了。去搜搜别的博客吧
但如果C有保护或者私有成员,D就不能有了。D可以直接用C的公有成员,间接用保护或私有成员。耦合度来讲组合不如继承,但组合更*,修改C的保护或者私有成员不会影响到D,但修改A的成员有可能全部都影响到B。但继承不可少,面向对象语言的三大特性中就有继承,其中的多态也是基于继承而存在的。
结束。