----------------"我离孤单几公里"
(1)继承概念以及定义:
继承设计,在于让代码实现复用。被继承的类,称为“父类”或者“基类”。 继承类,称为“子类”或者“派生类”。
①语法
//继承语法
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age;" << _age<< endl;
}
int _age=23;
string _name="peter";
};
//继承
class Student : public Person
{
private:
int _sid;//学豪
};
//继承
class Teacher :public Person
{
private:
int _tid;
};
可以从上面看出,学生和老师 都有共性在人的类里面。所以学生和老师可以继承人里的年龄、姓名。
并可以进行改变。
②继承权限:
可以看出,继承方式就是权限大小: private < protected < pubilc
类成员/继承方式 | public继承 | protected继承 | private继承 |
基类public | public | public | public |
基类protected | protected | protected | private |
基类private | 派生类不可见 | 派生类不可见 | 派生类不可见 |
简单来说就是取最小。
(2)基类和派生类赋值转换:
派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用
其原理在于切割(切片);
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age;" << _age<< endl;
}
private:
int _age=23;
string _name="peter";
string number = "23456789";
};
class Student : public Person
{
public:
private:
int _sid;
};
子类可以赋值 父类/父类指针/父类引用
(3)继承中的作用域:
子类父类的作用于具有独立性。当从父类继承下来的变量名,与子类成员变量同名时,会屏蔽父类的同名成员。
这种做法叫做隐藏, 也叫作重定义。
隐藏变量;
隐藏函数;父类子类func函数重名
class Person
{
public:
int _nums = 999;
void func()
{
cout << "Person::" << endl;
}
};
class Student : public Person
{
public:
void Print()
{
cout << "students:" << _nums << endl;
cout << "peson:" << Person::_nums << endl;
}
void func()
{
cout << "Student" << endl;
}
private:
int _nums = 100;
int _sid;
};
(4)派生类默认成员函数:
派生类默认成员函数,和类的默认成员函数一样。编译器会自动生成。
下面也就围绕六点来展开讨论;
1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
①构造
继承下来的基类对象,用基类对象的构造函数构造。
class Person
{
public:
Person(const char* name="peter")
:_name(name)
{
cout << "Person" << endl;
}
string _name;
};
class Student : public Person
{
public:
Student(const char* name,int id)
:Person(name)
,_sid(id)
{
cout << "Student()" << endl;
}
protected:
int _sid=1;
};
②拷贝构造:
class Person
{
public:
Person(const char* name="peter")
:_name(name)
{
cout << "Person()" << endl;
}
//s1(s2) p=s1
Person(const Person& p)
:_name(p._name)
{
cout<<"Person(const Person& p)" << endl;
}
string _name;
};
class Student : public Person
{
public:
Student(const char* name,int id)
:Person(name)
,_sid(id)
{
cout << "Student(const char* name,int id)" << endl;
}
Student(const Student& s)
: Person(s) //这里发生切片 切割
, _sid(s._sid)
{
cout << "Student(const Student& s)" << endl;
}
protected:
int _sid=1;
};
③赋值运算符:
class Person
{
public:
Person(const char* name="peter")
:_name(name)
{
cout << "Person()" << endl;
}
//s1(s2) p=s1
Person(const Person& p)
:_name(p._name)
{
cout<<"Person(const Person& p)" << endl;
}
//s1 = s2
Person& operator=(const Person& p)
{
cout << "Person& operator=(const Person& p)" << endl;
if (this != &p)
{
_name = p._name;
}
return *this;
}
string _name;
};
class Student : public Person
{
public:
Student(const char* name,int id)
:Person(name)
,_sid(id)
{
cout << "Student(const char* name,int id)" << endl;
}
Student(const Student& s)
: Person(s) //这里发生切片 切割
, _sid(s._sid)
{
cout << "Student(const Student& s)" << endl;
}
Student& operator=(const Student& s)
{
cout << "Student& operator=(const Student& s)" << endl;
if (this != &s)
{
//这里一定不要忘了 指定作用域 否则会调用子类的 operator
Person::operator=(s); //去赋值调用 父类的operator
_sid = s._sid;
}
return *this;
}
protected:
int _sid=1;
};
④析构:
class Person
{
public:
Person(const char* name="peter")
:_name(name)
{
cout << "Person()" << endl;
}
//s1(s2) p=s1
Person(const Person& p)
:_name(p._name)
{
cout<<"Person(const Person& p)" << endl;
}
//s1 = s2
Person& operator=(const Person& p)
{
cout << "Person& operator=(const Person& p)" << endl;
if (this != &p)
{
_name = p._name;
}
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
string _name;
};
class Student : public Person
{
public:
Student(const char* name,int id)
:Person(name)
,_sid(id)
{
cout << "Student(const char* name,int id)" << endl;
}
Student(const Student& s)
: Person(s) //这里发生切片 切割
, _sid(s._sid)
{
cout << "Student(const Student& s)" << endl;
}
Student& operator=(const Student& s)
{
cout << "Student& operator=(const Student& s)" << endl;
if (this != &s)
{
//这里一定不要忘了 指定作用域 否则会调用子类的 operator
Person::operator=(s); //去赋值调用 父类的operator
_sid = s._sid;
}
return *this;
}
~Student()
{
//父子析构 各自调用各自的
Person::~Person();
cout << "~Student()" << endl;
}
protected:
int _sid=1;
};
对于析构函数,值得注意的是,我们需要指定作用域!去调用析构(会在多态进行 分析)。
此外,为了保证先析构子类,再析构父类。所以会自动在析构子类后,调用父类的析构函数。
(5)继承和友元:
class A
{
//声明友元
friend void Print(A& a);
private:
int _a=3;
};
class B :public A
{
protected:
int _b;
};
void Print(A& a,B& b)
{
cout << a._a << endl;
cout << b._b << endl;
}
友元不能继承,基类友元不能访问子类保护、私有成员。
(6)继承与静态成员:
class Person
{
public:
Person()
{
_count++;
}
protected:
string _name;
public:
static int _count;
};
//静态成员变量初始化
int Person::_count = 0;
class Student:public Person
{
protected:
int _sid;
};
(7)C++缺陷:多继承
c++设计的一个坑在于 多继承。不仅仅是理解起来复杂,且必然会造成菱形继承。
单继承:
多继承:一个子类,有多个父类
菱形继承:
菱形继承的缺陷: ①数据冗余 ②二义性。
class A
{
public:
int _a;
};
class B : public A
{
public:
int _b;
};
class C :public A
{
public:
int _c;
};
class D :public B, public C
{
public:
int _d;
};
此状态下 ABCD是菱形继承。
这就是数据冗余,因为B类有一个父类的a C类也有一个父类的a。产生对a的二义性。要分开需要限定作用域。
bc的修改。
从监视窗口难以清晰得看出数据冗余。
为了解决这个问题,c++引入了virtual关键字(虚继承 !=虚函数)
继承
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;
};
从监视窗口看,这里两个类,已经是只有一份单独的父类变量 a 了。
从内存来看,划分了部分区域。但是很不解的是,为什么会有地址出现?那么针对同一份a,bc又是如何找到的呢?
通过B、C两个指针,指向一张表。B\C指针此时也叫作虚基表指针。 这个表就叫做虚基表,记录找到唯一 一份A地址处的偏移量。
感谢你的阅读
祝你好运~