1、基类和派生类对象赋值转换
派生类对象可以赋值给,基类的对象 / 基类的指针 / 基类的引用。---切片(把派生类中父类那部分切片赋值过去)。
继承天生支持向上转换,即 子类对象 ------> 父类对象/引用/指针。
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
//protected:
string _name = "peter"; // 姓名
int _age = 18; // 年龄
};
class Student : public Person
{
protected:
int _stuid; // 学号
};
class Teacher : public Person
{
protected:
int _jobid; // 工号
};
int main()
{
Person p;
Student s;
// 赋值兼容转换(切割,切片)
//p = s;
Person p1 = s;
Person& rp = s;
rp._name = "张三";
Person* ptrp = &s;
ptrp->_name = "李四";
return 0;
}
下面进行在监视窗口进行展示:
可以看到当:子类对象 ------> 父类引用/指针。rp和ptrp以及s指向同一空间,当改变_name时候,三者同时发生改变。
父类对象无法赋值给子类对象。
2、 继承中的作用域
a、在继承的体系结构中,基类和派生类都有自己独立的作用域。
b、当子类和父类当中有同名的成员时,子类成员将屏蔽父类对同名成员的直接访问,也叫隐藏/重定义。
c、函数名相同,构成成员函数的隐藏。
class Person
{
public:
void fun()
{
cout << "Person::func()" << endl;
}
protected:
string _name = "小李子"; // 姓名
int _num = 111; // 身份证号
};
// 隐藏/重定义:子类和父类有同名成员,子类的成员隐藏了父类的成员
class Student : public Person
{
public:
void fun()
{
cout << "Student::func()" << endl;
}
void Print()
{
cout << " 姓名:" << _name << endl;
cout << _num << endl;
cout << Person::_num << endl;
}
protected:
int _num = 999; // 学号
};
int main()
{
Student s;
s.Print();
s.fun();
s.Person::fun();
return 0;
}
3 、派生类的默认成员函数
a、对于派生类的构造函数/拷贝构造/赋值重载,必须调用基类的构造函数/拷贝构造/赋值重载去初始化基类的那一部分成员。
b、如果基类没有默认构造函数的时候,则必须在派生类构造函数的初始化列表显示调用。
c、对于析构函数,先调用子类的析构函数,再调用父类的析构函数。且编译器会对析构函数的名字进行特殊处理,处理成destrutor()。
class Person
{
public:
//Person(const char* name = "peter")
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;
delete _pstr;
}
protected:
string _name; // 姓名
string* _pstr = new string("111111111");
};
class Student : public Person
{
public:
// 先父后子
Student(const char* name = "张三", int id = 0)
:Person(name)
,_id(0)
{}
Student(const Student& s)
:Person(s)
,_id(s._id)
{}
Student& operator=(const Student& s)
{
if (this != &s)
{
Person::operator=(s);
_id = s._id;
}
return *this;
}
~Student()
{
// 由于后面多态的原因(具体后面讲),析构函数的函数名被
// 特殊处理了,统一处理成destructor
// 显示调用父类析构,无法保证先子后父
// 所以子类析构函数完成就,自定调用父类析构,这样就保证了先子后父
//Person::~Person();
cout << *_pstr << endl;
delete _ptr;
}
protected:
int _id;
int* _ptr = new int;
};
int main()
{
Student s1;
Student s2(s1);
Student s3("李四", 1);
s1 = s3;
return 0;
}
4、继承与友元
友元关系不能继承。
5、继承与静态成员
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。
// 静态成员属于父类和派生类
// 在派生类中不会单独拷贝一份,继承的使用权
class Person
{
public:
Person() { ++_count; }
//protected:
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 << Person::_count << endl;
cout << &p._name << endl;
cout << &s._name << endl;
cout << &p._count<< endl;
cout << &s._count << endl;
cout << &Person::_count << endl;
cout << &Student::_count << endl;
return 0;
}
6、菱形继承和菱形虚拟继承
菱形继承会出现数据冗余(数据二义性问题)。
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;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
D d1;
return 0;
}
a、对于菱形继承,数据存储的结构模型。