文章目录
前言
环境:VS2019版本!!!
class Dog
{
public :
Dog(string name, string sex, int age) :_name(name),_sex(sex),_age(_age)
{
}
void Eat()
{
cout << _name << "在吃东西" << endl;
}
void Sleep()
{
cout << _name << "在睡觉" << endl;
}
void Run()
{
cout << _name << "在跑" << endl;
}
string _name;
int _age;
string _sex;
};
class Cat
{
public:
Cat(string name, string sex, int age) :_name(name), _sex(sex), _age(_age)
{
}
void Eat()
{
cout << _name << "在吃东西" << endl;
}
void Sleep()
{
cout << _name << "在睡觉" << endl;
}
void Run()
{
cout << _name << "在跑" << endl;
}
string _name;
int _age;
string _sex;
};
细心的铁铁,一定发现了在以上Dog和Cat两个类,都有写Eat,Sleep,Run方法和相同的属性
使得代码出现了极大冗余性。若在增添一个小猪类,结果可想而知!!
而继承则大大解决了这个问题!!!
1、继承的概念及定义
1.1继承的概念
继承机制是面向对象程序设计是代码可以复用的最重要手段,它允许程序员在保持原有类特性的基础上进行扩展(即增加新的类特有的功能及属性),这样产生的新类,称作派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程,继承是类设计层次的复用。
1.2 继承的定义
1.2.1定义格式
下面我们看到的Animal是父类,也称作基类。Dog类是子类,也称做派生类
1.2.2继承关系和访问限定符
1.2.3继承基类成员访问方式的变化
注意:
1.基类私有成员,对子类对象不可见,但是此私有成员都同公有和保护的成员被继承下来了
2.使用关键字class时默认继承方式为private,关键struct时默认继承方式为public,故在继承环境下,应当显示提供继承方式
2、基类和派生类对象赋值关系
提示:
在public继承关系下,对象赋值关系,又名赋值兼容规则。
class Animal
{
public:
string _name;
int _age;
string _sex;
};
class Dog:public Animal
{
public :
string _color;
};
a.用子类对象给基类对象赋值
b.用基类对象指针指向子类对象
c.用基类对象引用引用子类对象
3、继承中的作用域
注: 在继承体系中,基类和子类都有独立的作用域,即两者作用域不相同
3.1 同名隐藏||重定义
同名隐藏,又名重定义,意在不同作用域下(即继承中的基类和子类)存在相同名称的成员,子类成员将屏蔽基类对同名成员的访问
//
//
//A中的变量_a和函数f1()与B中的相同名称成员构成了隐藏
class A {
public:
void f1()
{
cout << "A::f1()" << endl;
}
int _a;
};
class B :public A
{
public:
void f1(double a)
{
cout << "B::f1()" << endl;
}
double _a;
};
注意:
1.子类对象只能访问本类的相同名称成员,不能访问基类及基类以上的继承的同名变量
2.若要访问基类,则需加访问限定符
class A {
public:
void f1(int a)
{
_a = a;
}
int _a;
};
class B :public A
{
public:
void f1(int a,int b)
{
_a = a;
_b = b;
}
int _b;
};
4.派生类的默认成员函数
1.构造函数(普通继承下)
A.基类构造方法(没有显示提供/无参/全缺省),子类的构造方法可不提供
B.基类的构造方法非默认且带参数,子类的构造方法必须提供,且子类构造方法初始化列表需要显式调用基类的构造方法
C.未完待续…
2.拷贝构造函数
A.当涉及资源管理是,子类基类的拷贝构造函数都需要显示提供,否则会发生浅拷贝,反之则不显示提供。
class A {
public:
A(int a = 0) :_a(a)
{
}
A(const A& aa):_a(aa._a)
{
}
int _a;
};
class B :public A
{
public:
B(int a,int b):A(a),_b(b)
{
arr = new int [10];
}
~B()
{
delete[] arr;
}
//B(const B& bb) :A(bb), _b(bb._b)
//{
// arr=new int[10]
//}
int _b;
int* arr;
};
变量boy和boy1的arr变量指向同一份空间发生了浅拷贝
B.子类间的赋值分为基类部分成员变量的赋值和子类新增部分变量赋值(但涉及基类部分成员的赋值可采用,访问限定符的方式)
class A {
public:
A(int a = 0) :_a(a)
{
}
A(const A& aa):_a(aa._a)
{
}
A& operator=(const A& aa)
{
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
int _a;
};
class B :public A
{
public:
B(int a,int b):A(a),_b(b)
{
arr = new int [10];
}
~B()
{
delete[] arr;
}
B(const B& bb) :A(bb), _b(bb._b)
{
arr = new int[10];
}
B& operator=(const B& bb)
{
if (this != &bb)
{
//用bb中A的部分给*this中A的部分赋值
//*this = bb;此方法会陷入该赋值运算符重载方法的无限递归
A::operator=(bb);
//子类新增部分赋值
_b = bb._b;
delete[] arr;
arr = new int[10];
//bb.arr空间中的内容拷贝刀arr中
for(int i=0;i<d.arr.size();i++)
{
arr[i]=bb.arr[i];
}
}
}
public:
int _b;
int* arr;
};
3.析构函数
class A {
public:
A()
{
cout << "A::A()" << endl;
}
~A()
{
cout << "A::~A()" << endl;
}
};
class B :public A
{
public:
B()
{
cout << "B::B()" << endl;
}
~B()
{
cout << "B::~B()" << endl;
}
};
int main()
{
B();
return 0;
}
该代码中,在对B类析构时,会先调用~B(),在打印完成之后,编译器会自动调用一条汇编指令,
call A::~A(),即调用基类的析构函数,对从基类继承下来的部分的成员进行销毁。