认识继承
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
不写继承方式默认为私有private类型的
如果将类改为struct声明的则就会变为共有的public
继承类是不可访问父类的私有成员的,即使继承方式为共有的也不可以。访问的优先级是已最低的为主如图:
//继承
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
//protected:
private:
string _name = "peter"; // 姓名
int _age = 18; // 年龄
};
//不写继承方式默认为私有private类型的
//如果将类改为struct声明的则就会变为共有的public
class Student :public Person
{
public:
void func()
{
// 父类私有成员,子类用不了(无论什么方式继承)
/*cout << "name:" << _name << endl;
cout << "age:" << _age << endl;*/
}
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Student s;
Teacher t;
s.Print();
//s.func();
t.Print();
return 0;
}
继承之间的关系
. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认
的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
子类对象和父类对象赋值问题
C++规定,当子类对象赋值给父类的时候,不会产生临时变量
#include <iostream>
#include <string>
using namespace std;
//继承
int main()
{
Person p;
Student s;
//赋值兼容转换(切割,切片)
p = s;
return 0;
}
在进行赋值时,一般情况只允许子类赋值给父类(向上调整),如果需要父类赋值给子类时(向下调整),父类的定义需要时引用或者指针。
#include <iostream>
#include <string>
using namespace std;
//继承
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
//private:
string _name = "peter"; // 姓名
int _age = 18; // 年龄
};
class Student :public Person
{
public:
void func()
{
// 父类私有成员,子类用不了(无论什么方式继承)
/*cout << "name:" << _name << endl;
cout << "age:" << _age << endl;*/
}
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Person p;
Student s;
//赋值兼容转换(切割,切片)
p = s;
//向上取整
//s = p;//报错;
//向下取整,一般只有引用和指针类型才可以向下取整
Person p1 = s;
Person& rp = s;
rp._name = "张三";
Person* ptrp = &s;
ptrp->_name = "李四";
return 0;
}
赋值兼容转换(切割,切片)
继承的作用域
跟普通变量一样,C++也是遵守就近原则的,如果自己类里面没有才会找父类的。
如果想要用父类里的变量需要在调用变量前边加父类名字::
class Person
{
protected:
string _name = "小李子"; // 姓名
int _num = 111;
// 身份证号
};
class Student : public Person
{
public:
void Print()
{
cout << " 姓名:" << _name << endl;
cout << " 子类数字:" << _num << endl;
cout << " 父类数字:" << Person::_num << endl;
}
protected:
int _num = 999; // 学号
};
int main()
{
Student s;
s.Print();
return 0;
}
通常我们称这种行为为隐藏或重定义:子类和父类有同名成员,子类的成员隐藏了父类的成员
继承的成员函数也会出现隐藏或者重定义现象,我们需要在调用的函数名前加
父类名字::
如果函数名和父类的函数名一样就会构成重定义,不管有没有参数
class Person
{
public:
void fun()
{
cout << "父类" << endl;
}
protected:
string _name = "小李子"; // 姓名
int _num = 111;
// 身份证号
};
class Student : public Person
{
public:
void fun()
{
cout << "子类" << endl;
}
void Print()
{
cout << " 姓名:" << _name << endl;
cout << " 子类数字:" << _num << endl;
cout << " 父类数字:" << Person::_num << endl;
}
protected:
int _num = 999; // 学号
};
int main()
{
Student s;
s.Print();
s.Person::fun();
return 0;
}
子类和父类的成员函数关系
子类在初始化的时候,一定会调用父类的构造函数的。
class Person
{
public:
Person(const char* name = "peter")
: _name(name)
{
cout << "Person()" << endl;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name; // 姓名
};
class Student : public Person
{
public:
// 先父后子
Student(const char* name = "张三", int id = 0)
:_id(0)
{}
protected:
int _id;
};
int main()
{
Student s1;
return 0;
}
定义父类的成员时,我们可以直接通过子类来调用父类的匿名构造函数来进行初始化。
如果子类没有写父类成员的拷贝构造方法的话,会默认调用父类的构造方法
如果不想调用父类的默认构造方法的话需要自己实现
子类调用赋值操作的时候,如果子类里没有调用父类的赋值操作,也不会自动调用父类的,没有调用就是没有调用
子类父类析构问题
继承和友元
友元函数是不支持继承的,例如叔叔的朋友不是我的朋友,除非他成为我的朋友
继承静态成员之间的关系
静态成员属于父类和派生来
在派生类中不会单独拷贝一份,继承的是使用权
class Person
{
public:
Person() { ++_count; }
public:
string _name; // 姓名
public:
static int _count; // 统计人的个数。
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum; // 学号
};
int main()
{
Person p;
Student s1;
Student s2;
Student s3;
cout << &s1._name << endl;
cout << &p._name << endl;
cout << &s1._count << endl;
cout << &p._count << endl;
cout << &Person::_count << endl;
cout << &Student::_count << endl;
cout << " 人数 :" << Person::_count << endl;
cout << " 人数 :" << Student::_count << endl;
}
菱形继承及菱形虚拟继承
当这里的as进行调用时,由于继承的类都统一继承了另一个父类,导致共有变量age有两个,当我们访问时,就会出现以下报错
class Person
{
public:
string _name;
int _age;
};
class Student : public Person
{
protected:
int _num;
};
class Teacher : public Person
{
protected:
int _id;
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse;
};
int main()
{
Assistant as;
as._age = 18;
return 0;
}
解决方法一:
我们可以在变量名前面指定那个类的变量
这种方法只能解决眉下之急,所以也不是很实用,如果要彻底解决我们需要使用虚拟函数解决virtual
解决方法二:
**
**