现在也是结束了初阶部分的内容,今天开始就进入进阶部分了。一刻也没有为初阶的结束而哀悼,立刻赶来“战场”的是进阶部分里的继承
继承(inheritance)是一种面向对象编程的机制,允许一个类(派生类)从另一个类(基类)继承属性和行为。 继承机制是面向对象程序设计中使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "Nero";
int _age = 18;
};
class Teacher :public Person//公有继承
{
protected:
int _jobid;//老师的工号
};
class Student :public Person
{
protected:
int _stuid;//学生的学号
};
int main()
{
Student s;
Teacher t;
s.Print();
t.Print();
return 0;
}
class Base {
// 基类的成员和方法
};
class Derived : [访问权限] Base {
// 派生类的成员和方法
};
class Base
表示基类的定义。class Derived : [访问权限] Base
表示派生类的定义,并指定基类为 Base
。[访问权限]
可选,表示继承的访问权限,可以是 public
、protected
或 private
。如果不指定,默认为 private
。public
成员在类的内部和外部都可以被访问。public
成员。protected
成员在类的内部可以被访问,但在类的外部不能直接访问。protected
==成员。private
成员只能在类的内部被访问,外部不能直接访问。private
成员。所以,protected和private的访问权限相同(针对本类内外来说),但是二者对继承的子类来说是不一样的 相对应的,访问限定符有三个。那么继承方式也是有三种继承:
类成员/继承方式 | public继承 | protected继承 | private继承 |
---|---|---|---|
基类的public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private成员 |
基类的protected成员 | 派生类的protected成员 | 派生类的protected成员 | 派生类的private成员 |
基类的private成员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可见 |
class A
{
public:
int _public;
protected:
int _protected;
private:
int _private;
};
class B :public A
{
public:
void print_public()
{
cout << _public << endl;
}
void print_protected()
{
cout << _protected << endl;
}
void print_private()
{
cout << _private << endl;
}
};
int main()
{
B b;
return 0;
}
这里可以看到已经报错了,因为基类的private成员在派生类中是不可见的
class Student : public Person//学生类继承于Person类
{
public:
int _No; // 学号(学生类自己的成员变量)
};
void test1()
{
Student stu;
//子类对象可以赋值给父类对象/指针/引用
Person per = stu;//走了一个特殊处理:中间不会产生一个临时变量
Person* pper = &stu;//这个指针也是直接指向对象费父类那一部分
Person& rper = stu;
//基类对象不能赋值给派生类对象
stu = per;
//基类的指针可以通过强制类型转换赋值给派生类的指针
pper = &stu;
Student * ps1 = (Student*)pper; // 这种情况转换时可以的。
ps1->_No = 10;
pper = &per;
Student* ps2 = (Student*)pper; // 这种情况转换时虽然可以,但是会存在越界访问的问题
ps2->_No = 10;
}
int main()
{
test1();
return 0;
}
可以看到,有些地方是报错了的
class Person
{
protected:
string _name = "Neor"; // 姓名
int _id = 111;//身份证号
};
class Student : public Person
{
public:
void Print()
{
cout << "学号:" << _id << endl;
cout << "身份证号" << Person::_id << endl;
}
protected:
int _id = 888; // 学号
};
void test1()
{
Student s1;
s1.Print();
};
int main()
{
test1();
return 0;
}
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);//直接operator =(s)会因为隐藏,一直调用
_num = s._num;
}
return *this;
}
~Student()
{
cout << "~Student()" << endl;
}
protected:
int _num; //学号
};
void test2()
{
Student s1("Nero", 18);
}
int main()
{
test2();
return 0;
}
派生这些默认成员函数规则,其实跟以前类似, 唯一不同的是,不管是构造初始化/拷贝/析构,多了父类那一部分。原则: 父类那部分调用父类的对应函数完成
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
class A
{
public:
int _pa;
friend void print(B& bb);//这个print是A的友元
protected:
int _a;
};
class B :public A
{
public:
int _pb;
protected:
int _b;
};
void print(B& bb)
{
cout << "公有的_pb" << bb._pa;
cout << "私有的_b" << bb._b;
//cout << "公有的_pb" << bb._pb;
//cout << "私有的_b" << bb._b;
}
class A
{
public:
int _pa;
friend void print(B& bb);//这个print是A的友元
protected:
int _a;
};
class B :public A
{
public:
int _pb;
friend void print(B& bb);
protected:
int _b;
};
void print(B& bb)
{
cout << "公有的_pb" << bb._pa;
cout << "私有的_b" << bb._b;
//cout << "公有的_pb" << bb._pb;
//cout << "私有的_b" << bb._b;
}
这样才对
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例
class Person
{
public:
Person()
{
++_count;
}
protected:
string _name; // 姓名
public:
static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum; // 学号
};
int main()
{
Person p1;
Person p2;
Student s2;
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
就有可能出现一种特殊情况:菱形继承
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份
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 test3()
{
Assistant a;
a._name = "Nero";
}
int main()
{
test3();
return 0;
}
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承(在:后面加上virtual),即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
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 test3()
{
Assistant a;
a._name = "Nero";//此时就可以了
}
int main()
{
test3();
return 0;
}
这里为了探讨这个问题,先给出下列代码:
class A
{
public:
int _a;
};
class B : virtual public A
{
public:
int _b;
};
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;
}
D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A
继承讲完了,那下次肯定就是多态啦!!!感谢大家支持!!!