目录
继承的概念和定义
class Person
{
public:
void Print()
{
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "peter";
int _age = 18;
};
//Student继承了Person,Student中就拥有了Person的成员
//person 叫父类/基类
//student叫子类/派生类
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;
}
继承中的作用域
B中的fun和A中的fun不是构成重载,因为不是在同一个作用域
B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏
//A和B类的两个fun构成隐藏关系,继承中函数名相同就是隐藏
class A
{
public:
void fun()
{
cout << "func()" << endl;
}
};
class B :public A
{
public:
void fun(int i)
{}
};
void Test()
{
B b;
b.fun(10);
b.fun();
//想调用父类的=>b.A::fun()
}
int main()
{
Test();
return 0;
}
派生类的默认成员函数
构造函数:
要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
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;
}
protected:
string _name; // 姓名
};
class Student :public Person
{
public:
//子类构造函数——我们不写,编译器默认生成
//1.继承的父类成员---作为一个整体调用父类的构造函数初始化
//2.自己的内置类型成员---不处理(除非给了缺省值)
//3.自己的自定义类型成员---调用它的默认构造函数
//我们要自己实现子类构造函数
Student(const char*name, int id, const char* address)
//要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
:Person(name)
, _id(id)
, _address(address)
{}
private:
int _id;//内置类型
string _address;//自定义类型
};
int main()
{
Student s("黄振盛",1,"厦门市");
return 0;
}
析构函数:
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;
}
protected:
string _name; // 姓名
};
class Student :public Person
{
public:
//子类拷贝构造函数——我们不写,编译器默认生成
//1.继承的父类成员作为一个整体---调用父类的拷贝构造
//2.自己的内置类型成员---调用它的拷贝构造
//3.自己的自定义类型成员---值拷贝
Student(const char*name, int id, const char* address)
//要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
:Person(name)
, _id(id)
, _address(address)
{}
//自己实现拷贝构造
Student(const Student& s)
:Person(s)
,_id(s._id)
, _address(s._address)
{}
private:
int _id;//内置类型
string _address;//自定义类型
};
int main()
{
Student s1("小红",100,"厦门市");
Student s2(s1);
return 0;
}
赋值&析构函数
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;
}
protected:
string _name; // 姓名
};
class Student :public Person
{
public:
//子类拷贝构造函数——我们不写,编译器默认生成
//1.继承的父类成员作为一个整体---调用父类的拷贝构造
//2.自己的内置类型成员---调用它的拷贝构造
//3.自己的自定义类型成员---值拷贝
Student(const char*name, int id, const char* address)
//要注意的是父类成员是作为一个整体,调用父类的构造函数进行初始化
:Person(name)
, _id(id)
, _address(address)
{}
自己实现拷贝构造
//Student(const Student& s)
// :Person(s)
// ,_id(s._id)
// , _address(s._address)
//{}
//赋值:
Student& operator = (const Student& s)
{
if (this != &s)
{
_id = s._id;
_address = s._address;
Person::operator = (s);
}
return *this;
}
//析构
//子类拷贝构造函数——我们不写,编译器默认生成
//1.继承的父类成员作为一个整体---调用父类的析构函数
//2.自己的内置类型成员---不处理
//3.自己的自定义类型成员---调用它的析构函数
//自己实现
//子类析构函数和父类析构函数构成隐藏关系--why?
//因为编译器会对析构函数名做特殊处理,所有类的析构函数都会被统一处理成destructor()
//为什么编译器会做这个处理呢?——析构函数要构成多态重写
~Student()
{
Person::~Person();//子类的析构函数在执行结束后会自动调用父类的析构函数
}
private:
int _id;//内置类型
string _address;//自定义类型
};
int main()
{
Student s1("小红",100,"厦门市");
Student s2(s1);
Student s3("小蓝",200,"泉州市");
s1 = s3;
return 0;
}
复杂的菱形继承及菱形虚拟继承
先来看一段代码:
class B : public A
{
public:
int _b;
};
// class C : public A
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._a = 0;
return 0;
}
再看一段虚继承代码:
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;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
d._a = 0;
return 0;
}
将上面两段内存代码对比如下:
存了个偏移量
访问继承的虚基类对象成员,都是取偏移量计算
小结:
多继承带来的问题是菱形继承,菱形继承解决引来了虚拟继承,虚拟继承在腰部的位置!
如何解决:偏移量!
继承的总结和反思
1.最好不要设计出菱形继承
2.public继承是一种 is-a 的关系,组合是一种 has-a 的关系,尽量使用耦合度低(模块(类..)间关联度越低,独立性越高,耦合度越低)的组合