继承
面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果,当创建一个类时,不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类(父类),新建的类称为派生类(子类)。
格式 class son :public father{
}
继承方式
派生类继承基类会有三种继承方式,和访问权限类似,分别为public,protected和private,基类的私有成员不论是用什么方法来继承,派生类都不能访问,当用public继承时,成员的访问权限不变,当用protected继承时,除了private的权限外,都变为protected,当用private继承时,成员都变为私有private的。
举例
①用public继承时
#include <iostream>
#include <string>
using namespace std;
class father{
public:
int m_a=44;
protected:
int m_b=55;
private:
int m_c=66;
};
class son:public father{
};
void test(){
son s1;
s1.m_a=10;
//s1.m_b=20;非法操作
cout<<s1.m_a<<endl;
cout<<"基类大小是:"<<sizeof(son)<<endl;
}
int main(){
test();
return 0;
}
从这里可以看到,从基类public继承的,只能外部只能访问public权限的成员,并且虽然成员访问不到但是大小都继承下来了,大小为3*4=12个字节的大小。
②protected继承时
#include <iostream>
#include <string>
using namespace std;
class father{
public:
int m_a=44;
int m_d=77;
int m_e=88;
protected:
int m_b=55;
private:
int m_c=66;
};
class son:protected father{
public:
void func(){
cout<<"通过公共接口调用:"<<endl;
cout<<m_a <<m_d <<m_e <<endl;
}
};
void test(){
son s1;
s1.func();
//s1.m_a=10;都转化成了protected类外访问不到
//s1.m_b=20;
//cout<<s1.m_a<<endl;
cout<<"基类大小是:"<<sizeof(son)<<endl;
}
int main(){
test();
return 0;
}
③private继承
#include <iostream>
#include <string>
using namespace std;
class father{
public:
int m_a=44;
int m_d=77;
int m_e=88;
protected:
int m_b=55;
private:
int m_c=66;
};
class son:private father{
public:
void func(){
cout<<"通过公共接口调用:"<<endl;
cout<<m_a <<m_d <<m_e <<endl;
}
};
class grandSon:public son{
public:
void func(){
cout<<m_a<<m_d<<m_e<<endl;
}
};
void test(){
son s1;
s1.func();
grandSon gs;
gs.func();
//s1.m_a=10;
//s1.m_b=20;
//cout<<s1.m_a<<endl;
cout<<"基类大小是:"<<sizeof(son)<<endl;
cout<<"基类大小是:"<<sizeof(grandSon)<<endl;
}
int main(){
test();
return 0;
}
这个例子是编译不通过的,因为子类通过private继承后所有成员都是变成了私有,所以子类的子类再想调用就会报错了,不过虽然都不能访问,但是大小都被继承下来了,就像继承了一个密码箱有大小,但是你没有密码就是访问不了。
构造函数与析构函数
我们都知道,每一个类都有自己的构造函数和析构函数,所以我们可以讨论一下,当子类继承父类之后,他们的先后顺序是怎样的。
#include <iostream>
#include <string>
using namespace std;
class father{
public:
father(){
cout<<"父类的构造函数"<<endl;
}
~father(){
cout<<"父类的析构函数"<<endl;
}
};
class son:public father{
public:
son(){
cout<<"子类的构造函数"<<endl;
}
~son(){
cout<<"子类的析构函数"<<endl;
}
};
void test(){
son s1;
}
int main(){
test();
return 0;
}
对象重名
在代码量很大的时候,当我们使用继承时,很容易会在子类中和父类中定义相同的成员,那么当发生这样的情况时,该怎么处理或者说该怎么输出呢,这里我们可以来举例实测一下。
#include <iostream>
#include <string>
using namespace std;
class father{
public:
int money=1000;
void func(){
cout<<"这是父类的函数调用"<<endl;
}
};
class son:public father{
public:
int money=10000;
void func(){
cout<<"这是子类的函数调用"<<endl;
}
};
void test(){
son s1;
cout<<"直接调用"<<s1.money<<endl;
cout<<"加上域调用"<<s1.father::money<<endl;
s1.func();
s1.father::func();
}
int main(){
test();
return 0;
}
当子类与父类成员出现同名时,这时在调用时就会有区别,当调用子类成员时是正常调用,但是如果是想调用父类中的成员,就需要在调用时加上一个域::。所以这里类的大小也是子类成员大小+父类成员大小,在JAVA中一个类只允许单继承,而在C++中是可以允许多继承的,如果进行了多继承那么如果遇到了在两个父类中有重名的成员要调用其中一个父类中的成员也需要加上域符号,当然是不建议多继承的。
多继承语法:class son: public father1,public father2{ }
静态成员
静态成员的特点是所有对象共用同一个数据,在编译阶段就分配内存,也就是分配在全局区,在类内声明在类外初始化,成员函数也是所有对象共用一个静态成员函数,并且静态成员函数只能访问静态成员,那么在继承中,静态变量又是怎样继承的呢,同名静态成员又是如何调用的呢,这里可以举实例来测试一下。
#include <iostream>
#include <string>
using namespace std;
class father{
public:
static int money;
static void func(){
cout<<"这是父类的函数调用"<<endl;
cout<<"父类"<<money<<endl;
}
};
class son:public father{
public:
static int money;
static void func(){
cout<<"这是子类的函数调用"<<endl;
cout<<"子类"<<money<<endl;
}
};
int father::money=1000;
int son::money=200;
void test(){
son s1;
cout<<"直接调用"<<s1.money<<endl;
cout<<"加上域调用"<<s1.father::money<<endl;
s1.func();
s1.father::func();
son::func();
son::father::func();
cout<<sizeof(son)<<endl;
}
int main(){
test();
return 0;
}
经过测试我们可以发现,如果父子类出现同名的静态变量也是需要根据加不加域来引用哪个类的成员,并且因为静态变量是所有对象公用一份,所以可以不实例化对象也可以通过类名直接调用,并且这里注意,sizeof的大小是不会算静态变量的大小的,因为它是不论有没有实例化都在的数据,所以不占用对象的大小。