继承
继承的本质:代码的复用
单继承
格式
class 新类的名字:继承方式 继承类的名字{};
继承方式
- 基类中私有的在派生类中不可见 只有自己和友元能可见
- 基类中的成员(除私有成员外)在派生类中的权限总是以安全性高(基类中的访问限定符和派生类的继承方式哪个更小)的方式呈现
(安全性级别:private>protected>public)
class father
{
public:
int pub;
protected:
int pro;
private:
int pri;
};
class son1:public father //将基类除private外全部都继承过来 权限不变
{
// pub权限public pro权限protected pri不可见
};
class son2 :protected father //将基类除private外全部都继承过来 权限变成protected
{
// pub权限protected pro权限protected pri不可见
};
class son3 :private father //将基类除private外全部都继承过来 权限变成private
{
// pub权限private pro权限private pri不可见
};
多继承
一个类可以继承多个父类,可以从多个基类继承数据和函数。
格式:
class 新类的名字:继承方式1 继承类名1,继承方式2继承类名2 {};
注意:多个父类同名成员的二义性问题——作用域
class father
{
public:
int pub;
};
class mather
{
public:
int pub;
};
class son:public father,public mather{};//两个父类
int main()
{
son s1; //调用多个父类的同名成员编译报错
s1.father::pub = 1; //二义性解决:作用域
}
子类的初始化和销毁原则
创建子类对象,无论时使用子类指针还是父类指针,防止内存溢出需:
(1)初始化时,要先初始化父类,再初始化子类
为了初始化从父类继承来的成员,系统需要先调用其父类的构造函数,所以创建子类对象时:
- 基类有无参的构造函数,系统会先调用基类无参的构造函数
- 基类没有无参的构造函数,需使用初始化参数列表来显示调用有参的构造函数
(2)销毁时,要先销毁子类,再销毁父类
子类的析构函数会在执行完后调用父类的析构函数(子类析构函数最后会调用父类析构),从而确保资源能成功释放和销毁,而父类的析构函数不会调用子类的析构函数
即只需调用子类的析构函数就能按照顺序成功销毁。
- 栈区子类对象,栈区自动释放调用delete,delete在调用子类的析构函数,子类的析构函数尾会再调用父类的析构函数
- 子类指针指向子类对象,调用子类的析构函数后会自动调用父类的析构函数
- 父类指针指向子类对象
- 需要将父类析构函数写成虚析构函数,子类析构函数重写(因为编译器会把析构函数名字都处理成destructor),调用子类析构函数(结束后会自动调用父类的析构函数)
- 若未将父类的析构函数写成虚析构,则发生隐藏,调用(指针类型)父类析构,子类未被释放,造成内存泄露
案例
构造例1——基类有无参构造
class A
{
public:
A( )
{
cout << "A:无参构造" << endl;
}
A(int val)
{
cout << "A:有参构造" << endl;
}
};
class B :public A
{
public:
B()
{
cout << "B:构造" << endl;
}
};
int main()
{
B b;
}
/*
A:无参构造
B:构造
*/
构造例2——基类只有有参构造
class A
{
public:
A(int val)
{
cout << "A:只有有参构造" << endl;
}
};
class B :public A
{
public:
B():A(1) //构造函数参数列表显示调用父类构造
{
cout << "B:构造" << endl;
}
};
int main()
{
B b;
}
/*
A:只有有参构造
B:构造
*/
销毁例1——栈区子类对象
class A
{
public:
A()
{
cout << "A:构造" << endl;
}
~A()
{
cout << "A:析构" << endl;
}
};
class B:public A
{
public:
B()
{
cout << "B:构造" << endl;
}
~B()
{
cout << "B:析构" << endl;
//调用父类析构函数
}
};
int main()
{
B b; //栈区函数结束自动调用delete销毁 delete调用子类的析构函数
}
/*
A:构造
B:构造
A:析构
B:析构
*/
销毁例2——子类指针指向子类对象
class A
{
public:
A()
{
cout << "A:构造" << endl;
}
~A()
{
cout << "A:析构" << endl;
}
};
class B:public A
{
public:
B()
{
cout << "B:构造" << endl;
}
~B()
{
cout << "B:析构" << endl;
//调用父类析构函数
}
};
int main()
{
B* b=new B();
delete b; //正常调用子类析构函数
}
/*
A:构造
B:构造
A:析构
B:析构
*/
销毁例3——父类指针指向子类对象 虚函数未构成重写 函数隐藏 直接delete内存泄漏(delete会调用析构函数)
class A
{
public:
A()
{
cout << "A:构造" << endl;
}
~A()
{
cout << "A:析构" << endl;
}
};
class B:public A
{
public:
B()
{
cout << "B:构造" << endl;
}
~B() //函数隐藏 ~A不是虚函数
{
cout << "B:析构" << endl;
//调用父类析构函数
}
};
int main()
{
A* a = new B(); //父类指针指向子类对象
delete a; //函数隐藏 执行指针类型 A的析构 子类未释放 内存泄漏
}
/*
A:构造
B:构造
A:析构
*/
销毁例4——父类指针指向子类对象 虚函数构成重写 直接delete 成功销毁
class A
{
public:
A()
{
cout << "A:构造" << endl;
}
virtual ~A()
{
cout << "A:析构" << endl;
}
};
class B:public A
{
public:
B()
{
cout << "B:构造" << endl;
}
~B()
{
cout << "B:析构" << endl;
//调用父类析构函数
}
};
int main()
{
A* a=new B();
delete a; //正常调用子类析构函数
}
/*
A:构造
B:构造
A:析构
B:析构
*/