重载
函数重载
函数重载是指在同一个作用域内定义多个同名函数,但这些函数的参数列表(参数的数量或类型)必须不同。编译器通过参数列表来区分这些函数
运算符重载
操作对象在重载运算符之前
成员函数的重载与全局函数的重载
成员函数的重载和全局函数的重载是类似的,只是作用域不同,成员函数重载在类的范围内,而全局函数重载在在整个命名空间中
覆盖和隐藏
在C++中,覆盖指在派生类中重新定义已经存在的虚函数。这种机制允许派生类提供特定于该类的实现,而不是使用基类中的默认实现。覆盖是实现多态性的关键手段之一,多态性允许通过基类指针或引用来调用派生类中的特定函数
要实现覆盖,必须满足:
虚函数:基类中的函数必须是虚函数,这通常在函数声明前添加virtual关键字来实现
函数签名匹配:派生类中的函数必须与基类中的虚函数具有相同的函数签名,包括返回类型,函数名和参数列表
访问权限:派生类中的覆盖函数可以有不同的访问权限,(例如,可以是public而基类中的函数是protected),但通常它们会保持相同的访问权限以确保一致性
函数体:派生类中的覆盖函数必须提供自己的实现
#include <iostream>using namespace std;class Base {public:virtual void show() const {std::cout << "Base class show function" << std::endl;}// 虚析构函数确保派生类对象可以正确销毁 virtual ~Base() {}};class Derived : public Base {public:// 覆盖Base类中的show函数,派生类中的覆盖函数可以没有virtual关键字 void show() const {std::cout << "Derived class show function" << std::endl;}virtual void display(){cout << "Derived virtual void display()" << endl;}};int main() {Base* basePtr;Derived derivedObj;virtual void display(){cout << "Derived virtual void display()" << endl;}basePtr = &derivedObj;basePtr->show(); // 调用的是Derived类中的show函数,因为Derived类覆盖了Base类中的show函数 return 0;
}
在这个例子中,Derived类覆盖了Base类中的show函数。当通过基类指针basePtr调用show函数时,实际调用的 是Derived类中的show函数,因为Derived类的对象被绑定到了这个指针上,并且Derived类提供了show函数的 覆盖实现。
子类继承父类时,会把父类的虚函数表继承过来,子类会把自己的虚函数添加进虚函数表中,如果子类对父类的虚函 数进行重写,就会产生覆盖。
隐藏
在C++编程中,"隐藏"(hiding)通常指的是两个不同但相关的概念:成员函数隐藏(Function Hiding)和成员属 性隐藏(Member Hiding)。这些概念涉及到继承(inheritance)和多态(polymorphism)
成员函数隐藏
成员函数隐藏发生在派生类(derived class)中重新定义了一个与基类(base class)中同名但参数列表不同的 函数。这种情况下,基类的函数在派生类的作用域中被隐藏,而不是被重载(overloaded)
#include <iostream>using namespace std;class Base {public:void func() {cout << "Base::func()" << endl;}void func(int) {cout << "Base::func(int)" << endl;}};class Derived : public Base {public:void func(double) {cout << "Derived::func(double)" << endl;}};int main() {Derived d;// d.func(); // 编译错误:'func' 是未定义的标识符 d.func(1); // 调用 Derived::func(double) d.func(1.0); // 调用 Derived::func(double) Base* b = &d;b->func(); // 调用 Base::func() b->func(1); // 调用 Base::func(int) // b->func(1.0); // 编译错误:'class Base' 没有名为 'func' 的成员 }
在这个例子中,Derived 类中的 func(double) 隐藏了 Base 类中的 func() 和 func(int)。因此,在 Derived 类的对象上调用 func() 会导致编译错误,因为编译器在 Derived 类的作用域中找不到匹配的无参 func 函数。
成员属性隐藏
成员属性隐藏发生在派生类中定义了一个与基类中的成员属性同名的成员。这种情况下,基类的成员在派生类的作用 域中被隐藏
#include <iostream>
using namespace std;
class Base {
public: int value; void show() { cout << "Base::show(), value = " << value << endl; }
}; class Derived : public Base {
public: double value; void show() { cout << "Derived::show(), value = " << value << endl; }
}; int main() { Derived d; d.value = 3.14; d.show(); // Derived::show(), value = 3.14 Base* b = &d; b->value = 42; b->show(); // Base::show(), value = 42 cout << "Derived::value = " << d.value << endl; // Derived::value = 3.14 return 0;
}
在这个例子中,Derived 类中的 value 和 show 隐藏了 Base 类中的同名成员。因此,通过 Derived 类的 对象访问 value 和 show 时,将访问 Derived 类中的版本。但是,通过基类指针访问这些成员时,将访问基类 中的版本。‘
注意:
1. 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual关键字,基类的函数将 被隐藏
2. 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有 virtual关键字。此时基类的 函数被隐藏
抽象类
在C++中,抽象类是一种不能实例化的类,通常用于定义接口或基类,以便其他类可以继承并实现这些接口。抽象类 通常包含至少一个纯虚函数,即一个声明为= 0的虚函数。
1. 纯虚函数:纯虚函数是声明为= 0的虚函数,没有函数体。它要求派生类必须提供该函数的实现。
class AbstractClass {
public: virtual void pureVirtualFunction() = 0; // 纯虚函数
};
2. 不能实例化:由于包含纯虚函数,抽象类不能实例化。尝试实例化抽象类会导致编译错误
AbstractClass obj; // 错误:不能实例化抽象类
3. 作为基类:抽象类通常用作基类,派生类可以继承它并提供纯虚函数的实现
class DerivedClass : public AbstractClass {
public: void pureVirtualFunction() override { // 提供纯虚函数的实现 std::cout << "DerivedClass implementation of pureVirtualFunction" <<
std::endl; }
};
4. 抽象类的指针和引用:尽管不能直接实例化抽象类,但可以创建指向抽象类的指针或引用,并让它们指向派生 类的对象。
AbstractClass* ptr = new DerivedClass();
ptr->pureVirtualFunction(); // 调用派生类的实现
5. 抽象类的构造函数和析构函数:抽象类可以有构造函数和析构函数。这些函数在派生类的构造函数和析构函数中 会被调用。
6. 子类继承抽象类时必须实现抽象类里面的全部纯虚函数,否则子类也为抽象类
应用场景:
在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。此时我 们会在父类中只声明相应的方法,而不去具体实现,当子类继承父类时,让子类根据自己的实际情况去实现相应 的方法。
从多个具有相同特征的类中抽象出一个抽象类, 以这个抽象类作为子类的模板,从而避免子类设计的随意性
泛型
在C++中,泛型编程是一种允许代码在不同数据类型上工作的技术。C++ 提供了几种实现泛型编程的方法,其中最常 用的两种是模板(Templates)和泛型算法(通常与标准模板库STL,一起使用)
模板
模板是C++中实现泛型编程的主要工具。允许你编写与类型无关的代码,然后在编译时根据提供的类型实例化这些代 码。模板可以是函数模板或类模板。
// 定义一个 函数/类 模板
template <typename T[,typename K]>
template :声明创建模板
typename :表明其后面的符号是一种数据类型
T:通用的数据类型,名称可以任意,通常为大写字母,可以有多个,用逗号隔开
函数声明或定义的时候,用到的数据类型,可以用抽象数据类型T代替’
函数模板
函数模板允许你定义一个函数,其参数或返回类型在函数被调用时才确定。
#include <iostream>using namespace std;// 定义一个函数模板
template <typename T>T add(T a, T b) {return a + b;}int main() {cout << "Sum of integers: " << add(3, 4) << endl;cout << "Sum of doubles: " << add<double>(3.14, 2.71) <<endl;return 0;}
add 是一个函数模板,它可以接受任何类型 T 的两个参数,只要这个类型支持 + 操作符。
类模板
类模板允许你定义一个类,其成员变量的类型在对象被创建时才确定。
#include <iostream>using namespace std;// 定义一个类模板
template <typename T>class Box {private:T content;public:Box(const T &content);T getContent() {return content;}};
Box 是一个类模板,它可以存储任何类型 T 的内容
注意点
1. 类模板的定义使用template关键字开头,后面紧跟一个尖括号(< >),在尖括号内声明一个或多个模板参数 (通常是类型参数)。这些模板参数在类模板内部被用作类型占位符
template <typename T,typename K>
2. 类模板中成员函数在类外实现时,需要加上模板参数
template <typename T>Box<T>::Box(const T &content){this->content = content;}
类模板成员在类外定义的时候,格式比较麻烦,不推荐在类外定义,推荐直接在类内实现
类模板与继承
子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
若想灵活指出父类中T的类型,子类也需变成类模板
template <typename T>class Base{T m;};class Son : public Base //错误:未指定类型
{...};class Son : public Base<int> //正确:必须指定类型
{...};//类模板继承类模板,可用T2指定父类中的T类型
template <typename T1, typename T2>class Son:public Base<T2>{...};
auto自动推断类型
定义向量vector<int> nums ={1,5,6,8,7,9};
//打印所有变量
for(int i =0;i<num.size();i++){
}
(排序,向量)
STL 容器用于存储和管理数据。例如:
#include <iostream>
#include <vector>
using namespace std;
int main() { vector<int> vec = {1, 2, 3, 4, 5}; // 使用范围for循环遍历容器 for (int val : vec) { cout << val << " "; } cout << endl; return 0;
}
STL 算法用于对容器中的数据进行操作。例如
#include <iostream>
#include <vector>
#include <algorithm> // 包含sort算法
using namespace std;
int main() { vector<int> vec = {5, 2, 9, 1, 5, 6}; // 使用sort算法对容器进行排序 sort(vec.begin(), vec.end()); // 输出排序后的容器 for (int val : vec) { cout << val << " "; } cout << endl; return 0;
}
C++ 中的泛型编程通过使用模板和标准模板库(STL)来实现。模板允许你编写与类型无关的代码,而 STL 提供了 一组高效的、基于模板的数据结构和算法,使得编写高性能、可重用和可扩展的代码变得更加容易。
