13.继承(一)
一.继承的概念及定义
1.继承的概念
继承的本质就是进行复用
2.继承定义
class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "peter"; // 姓名int _age = 18; // 年龄
};// 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了
// Student和Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可
// 以看到变量的复用。调用Print可以看到成员函数的复用。
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;
}
3.继承方式的探讨
class Person
{
public:Person(){}void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;//cout << _tel << endl;}string _name = "peter"; // 姓名
protected:int _age = 18; // 年龄//...
private:// 父类定义本质,不想被子类继承int _tel = 110;};// 继承的父类的成员
class Student : public Person
{
public:void func(){// 子类用不了(不可见)// cout << _tel << endl;// 子类可以用cout << _name << endl;cout << _age << endl;}
protected:int _stuid; // 学号
};class Teacher : public Person
{
protected:int _jobid; // 工号
};int main()
{Person p;p.Print();Student s;s.Print();//s._name = "张三";Teacher t;//t._name = "张老师";return 0;
}
定义成为保护的(protected),对于类外面的(不能直接使用),对于子类而言(我们能使用protected的)
实践中,最常用的就是下面的方式
二.基类和派生类对象赋值转换
我们这里就理解是(多的能转换到少的地方,多的部分就不要就行了)
class Person
{
public:Person(){}void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;//cout << _tel << endl;}string _name = "peter"; // 姓名
protected:int _age = 18; // 年龄//...
private:// 父类定义本质,不想被子类继承int _tel = 110;};// 继承的父类的成员
class Student : public Person
{
public:void func(){// 子类用不了(不可见)// cout << _tel << endl;// 子类可以用cout << _name << endl;cout << _age << endl;}
protected:int _stuid; // 学号
};class Teacher : public Person
{
protected:int _jobid; // 工号
};int main()
{Student s;Person p;// 跟下面机制不一样// 特殊语法规则:不是类型转换,中间没有产生临时变量p = s;Person* ptr = &s;Person& ref = s;ptr->_name += 'x';ref._name += 'y';s._name += 'z';int i = 1234;printf("%x\n", i);// 类型转换// 截断char ch = i;printf("%x\n", ch);// 提升i = ch;printf("%x\n", i);const char& refch = i;return 0;
}
因为 ptr , ref 和 s 操作的是同一个东西
三.继承中的作用域
1.作用域讲解
// 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;}protected:int _num = 999; // 学号
};void Test()
{Student s1;s1.Print();
};
我们可以看到,在这里,我们Person和Student类中都有_num变量
当我们要访问的时候,到底是访问谁的呢?
没有就向上查找(没有就到,全局域进行查找)
2.题目判断
答案选择: B
class A
{
public:void fun(){cout << "func()" << endl;}
};class B : public A
{
public:void fun(int i){cout << "func(int i)->" << i << endl;}
};// 下面哪个是正确的()
// fun构成重载
// fun构成隐藏
// fun构成重写
// 编译报错
// 运行报错int main()
{B bb;bb.fun();return 0;
}
答案选择: B D
四.派生类的默认成员函数
1.构造函数
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
{
protected:int _x;string _address;
};// 父类成员(整体) -- 默认构造
// 子类自己的内置成员 -- 一般不处理
// 子类自己的自定义成员 -- 默认构造int main()
{Student s;return 0;
}
父类的已经写好了构造函数了(所以必定要调用父类的构造函数)
2.拷贝构造
Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Student(const Student& st):Person(st),_x(st._x), _address(st._address){}
3.赋值(operator=)
Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}Student& operator=(const Student& st){if (this != &st){Person::operator=(st);_x = st._x;_address = st._address;}return *this;}
4.析构函数
我们要是写呢?
~Person(){cout << "~Person()" << endl;delete[] _str;}// 由于多态,析构函数的名字会被统一处理成destructor()// 父类析构不能显示调用,因为显示调用不能保证先子后父~Student(){// 析构函数会构成隐藏,所以这里要指定类域//Person::~Person();cout << "~Student()" << endl;// delete [] _ptr;cout << _str << endl;}
我们子类不能调用父类的析构函数
但是,我们运行看到
析构函数会有一个机制(自动调用)
(其他的都要进行显示调用,但是析构函数是自动调用的)
C++永远是保证(构造函数)先父后子(继承父类,那么父类就在定义的最开始)(不是按照初始化列表顺序开始的)
析构的时候,是先子后父,所以父类的析构不是自己显示调用的,(子类析构结束后,自动调用)