一、继承的基本语法
语法:class 子类 : 继承方式 父类。
class Father
{
};
class Son : public Father
{
};
#include <string>
#include <iostream>
using namespace std;
class BasePage
{
public:
void header()
{
cout << "首页、登录、注册(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图(公共头部)" <<endl;
}
void line()
{
cout << "----------------------------------------------" <<endl;
}
};
class Java: public BasePage
{
public:
void content()
{
cout << "Java" <<endl;
}
};
class Cpp: public BasePage
{
public:
void content()
{
cout << "Cpp" <<endl;
}
};
void test01()
{
Java ja;
ja.header();
ja.content();
ja.footer();
ja.line();
Cpp cpp;
cpp.header();
cpp.content();
cpp.footer();
cpp.line();
}
int main()
{
test01();
system ("pause");
return 0 ;
}
继承的好处:减少重复的代码。
子类也成为派生类。
父类也成为基类。
派生类中的成员包含两大部分:一类是从基类继承过来的,表现派生类与基类的共性。一类是自己增加的成员,表现出自己的个性。
二、继承方式
三、继承中的对象模型
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son :public Base
{
public:
int m_D;
//父类中所有非静态成员属性都会被子类继承
//父类中私有成员是被编译器隐藏了,因此访问不到,但是确实被继承下去了
};
void test01()
{
cout << sizeof(Son)<<endl;
//16
}
int main()
{
test01();
system ("pause");
return 0 ;
}
/*
利用开发人员命令提示工具查看对象模型
跳转盘符 F:
跳转文件路径 cd 具体路径下
查看命名
cl /d1 reportSingleClassLayout类名 文件名
*/
查看后如下显示
四、父类子类中构造与析构的顺序
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base 构造" <<endl;
}
~Base()
{
cout << "Base 析构" <<endl;
}
};
class Son :public Base
{
public:
Son()
{
cout << "Son 构造" << endl;
}
~Son()
{
cout << "Son 析构" << endl;
}
};
void test01()
{
Son s;
//先构造父类,再构造子类,析构则相反
}
int main()
{
test01();
system ("pause");
return 0 ;
}
五、同名成员的处理
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
int m_A;
Base()
{
m_A = 100;
}
void func()
{
cout << "Base func()" <<endl;
}
void func(int a)
{
cout << "Base func(int a)" <<endl;
}
};
class Son :public Base
{
public:
Son()
{
m_A = 200;
}
int m_A;
void func()
{
cout << "Son func()" <<endl;
}
};
void test01()
{
Son s;
cout << s.m_A <<endl;
cout << s.Base::m_A <<endl;
}
void test02()
{
Son s;
s.func();
//s.func(1)
//如果子类中出现和父类同名的成员函数
//子类的同名成员会隐藏掉父类中所有的同名函数
//若想要访问隐藏的同名函数,加作用域即可
s.Base::func(1);
}
int main()
{
test02();
system ("pause");
return 0 ;
}
总结:
1.子类对象可以直接访问到子类中的同名成员。
2.子类对象加作用域可以访问到父类同名成员。
3.如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有的同名函数,若想要访问隐藏的同名函数,加作用域即可。
六、同名静态成员处理
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
static int m_A;
static void func()
{
cout << "Base Func()" <<endl;
}
};
int Base::m_A = 100;
class Son :public Base
{
public:
static int m_A;
static void func()
{
cout << "Son Func()" <<endl;
}
};
int Son::m_A = 200;
void test01()
{
//1、通过对象访问
Son s;
cout << s.m_A <<endl;
cout << s.Base::m_A <<endl;
//2、通过类名访问
cout << Son::m_A <<endl;
//第一个 :: 代表通过类名方式访问
//第二个 :: 代表访问父类作用域下
cout << Son::Base::m_A <<endl;
}
void test02()
{
//访问同名的静态函数
Son s;
s.func();
s.Base::func();
Son::func();
Son::Base::func();
}
//静态也适用同名隐藏的规则
int main()
{
test01();
system ("pause");
return 0 ;
}
总结:同名的静态成员处理方式与非静态成员的处理方式是一样的,只不过有两种访问方式,通过对象和通过类名。
七、多继承语法
#include <string>
#include <iostream>
using namespace std;
class Base1
{
public:
int m_A;
Base1()
{
m_A = 100;
}
};
class Base2
{
public:
int m_A;
Base2()
{
m_A = 200;
}
};
class Son :public Base1, public Base2
{
public:
int m_C;
int m_D;
Son()
{
m_C = 300;
m_D = 400;
}
};
void test01()
{
Son s;
cout << s.Base1::m_A <<endl;
cout << s.Base2::m_A <<endl;
//不建议使用多继承
}
int main()
{
test01();
system ("pause");
return 0 ;
}
八、菱形继承的问题以及解决方法
#include <string>
#include <iostream>
using namespace std;
class Animal
{
public:
int m_Age;
};
//利用虚继承,解决菱形继承的问题
//继承之前加上关键字 virtual
//Animal类称为虚基类
class Sheep : virtual public Animal{};
class Camel : virtual public Animal{};
class Alpaca : public Sheep, public Camel{};
void test01()
{
Alpaca al;
al.Sheep::m_Age = 18;
al.Camel::m_Age = 28;
//当进行了菱形继承后,两个父类拥有相同数据,需要加以作用域区分
cout << al.Sheep::m_Age <<endl;
cout << al.Camel::m_Age <<endl;
//菱形继承导致了数据有两份,资源浪费
//虚继承之后,资源只有一份,可以直接访问
cout << al.m_Age << endl;
}
int main()
{
test01();
system ("pause");
return 0 ;
}