容器适配器、模板类特化、函数特化、继承、组合
4-30第一次订改
容器适配器
stack与queue不被称为容器 这是因为它们是对 其他容器的接口实现了包装。
stack:
queue:
如图他们都是使用deque进行的封装结果。
所以称它们都为"容器适配器"即:将一种接口转换为另一种接口。
deque
两端开口的队列 只可在两端进出。
底层并非数组。是内存块间接相连

priority_queue介绍
优先级队列默认基于 vector 实现 也可以使用其他容器(支持随机迭代器->随机访问)实现 
它的第一个元素总是最大的元素。
但保留队列的尾入头出的性质 底层使用堆的向上调整算法(构造、push)与向下调整算法(pop)。
特化
模板内存在特化的概念,特化分作类特化、函数特化。特化是通过同名类、函数但参数存在区别的一种多模板形式。
特化的性质是实现同名类/函数的参数明确使得调用更加高效,非常适合于频繁调用的对象。
函数特化
特化就是在模板处给参(仅限于:整型 字符型 布尔型 ) 。 //浮点型C++20及以后
template<class T1>
bool Less(T1 x, T1 y)
{
return x < y;
}
template<>
bool Less<Date*>(Date* x, Date* y)
{
return x < y;
}
以上是全特化的例子,即模板参数全部明确显示。
函数的偏特化
函数特化不存在偏特化
函数特化常常遇到出乎意料的情况这时就常常使用重载函数来代替特化函数了。
因为类不存在重载而函数提供了重载因此就不需要"函数偏特化"要求了。
类特化
template<class T1, class T2>
class Data
{
public:
Data() {cout<<"Data<T1, T2>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
//类模板的偏特化 类似的还有全特化
template<class T>
class Data<T, char>
{
public:
Data() {cout<<"Data<T, char>" <<endl;}
private:
int _d1;
char _d2;
};
//两个类型也可偏特化为指针 引用
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
Data() {cout<<"Data<T1*, T2*>" <<endl;}
private:
T1 _d1;
T2 _d2;
};
继承
继承的基本使用方式:
class Person
{
public:
Person()
{}
protected:
int age;
private:
string gender;
};
class student :public Person
{
public:
student()
{}
protected:
int sID;
};
派生类strcut默认继承方式public。
class下默认继承方式private。
不写明继承方式时的默认的继承规则,类似于类的特化继承的子类下也是按需实例化。
当子类下无奈使用了与父类同名的变量时使用一定要指明类域。
凡子类下函数名与父类存在一致函数名时会发生隐藏(在进行子类默认函数的构造中要尤其注意)。
继承权限
※ 继承下派生类的访问成员的权限:

子类private访问下的报错:

final对继承的限制性
final关键字实现一个不能继承的类。
class Base final
{
public:
void func5() { cout << "Base::func5" << endl; }
protected:
int a = 1;
private:
};
基类友元不能访问派生类私有和保护成员。
类的友元
class A {
private:
int secret;
public:
A(int s) : secret(s) {}
// 声明 B 是友元类
friend class B;
};
class B {
public:
void showSecret(A& a) {
cout << "A的秘密是: " << a.secret << endl; // ✅ 可以访问
}
void modifySecret(A& a, int value) {
a.secret = value; // ✅ 可以修改
}
};
※单继承:
一个类只有一个直接基类的情况。

虚继承解决多对一的情况。

class Person
{
public:
string _name; // 姓名
// ...
};
// 使⽤虚继承Person类 // 使⽤虚继承Person类
class Student : virtual public Person class Teacher : virtual public Person
{ ******* { *******
protected: protected:
int _num; //学号 int _id; // 职⼯编号
}; };
菱形继承
菱形继承导致数据冗余与二义性问题强烈反对。
数据冗余:


二义性
bat内实例化数据类型为Animal的数据,编译器不知道是Bird还是Mammal。
※子类相关父类的默认成员函数建构注意事项:
1. 派⽣类的构造函数必须调用基类的构造函数初始化基类的那⼀部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
class Base {
public:
//非默认构造
Base(int x) {
cout << "Base 构造函数,x = " << x << endl;
}
};
class Derived : public Base {
public:
// 显式调用基类构造函数
Derived(int a) : Base(a) { // ← 初始化列表中调用
cout << "Derived 构造函数" << endl;
}
};
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
class Derived : public Base {
private:
int data;
public:
// 派生类的拷贝构造函数
Derived(const Derived& other)
: Base(other), // ← 显式调用基类的拷贝构造函数
data(other.data) {
cout << "Derived 拷贝构造" << endl;
}
};
3. 派⽣类的operator=必须要调用基类的operator=完成基类的复制。需要注意的是派生类的是 operator=隐藏了基类的operator=,所以显示调用基类的operator=,需要指定基类作用域。
4. 因为多态中⼀些场景析构函数需要构成重写,重写的条件之⼀是函数名相同(那么编译器会对析构函数名进⾏特殊处理,处理成destructor(),所以基类析构函数不加virtual的情况下,派⽣类析构函数和基类析构函数构成隐藏关系。(析构函数仅在子类拥有动态成员时才要写并且加virtual)
组合
class Engine {
public:
void start() { cout << "引擎启动" << endl; }
};
class Wheel {
public:
void rotate() { cout << "车轮转动" << endl; }
};
class Car {
private:
Engine engine; // Car 有一个 Engine
Wheel wheels[4]; // Car 有 4 个 Wheel
};
继承与组合
继承下以(is-a)。如:学生是人
组合时以(has-a)进行。如:车有轮胎
继承的耦合性高 组合的耦合性低 。在多数情况下组合较好于继承
※多继承下的指针偏移问题
继承声明也有顺序之说其决定了内存的布局构造先声明的先占据。
Derived先继承Base1后Base2:
内存布局:

更多推荐
所有评论(0)