【C++】多态

目录

[1. 多态的概念](#1. 多态的概念)

[1.1 静态多态(编译时多态)](#1.1 静态多态(编译时多态))

[1.2 动态多态(运行时多态)](#1.2 动态多态(运行时多态))

[2. override和final](#2. override和final)

[2.1 override:显式检测虚函数重写](#2.1 override:显式检测虚函数重写)

[2.2 final:禁止重写或继承](#2.2 final:禁止重写或继承)

[3. 多态的原理(vptr、vtable)](#3. 多态的原理(vptr、vtable))

[4. 含虚函数的对象的大小](#4. 含虚函数的对象的大小)

[5. 动态绑定与静态绑定](#5. 动态绑定与静态绑定)

[6. 经典面试题](#6. 经典面试题)

[7. 析构函数的重写](#7. 析构函数的重写)

[8. 协变](#8. 协变)

[9. 纯虚函数和抽象类](#9. 纯虚函数和抽象类)

[10. 虚函数表末尾的标记(了解)](#10. 虚函数表末尾的标记(了解))


1. 多态的概念

在C++中,多态(polymorphism)是面向对象编程的三大特性之一,核心思想是"一个接口,多种实现"------即同一操作作用于不同对象时,可产生不同的行为。
C++中的多态分为两类:静态多态 (编译时多态)和动态多态(运行时多态)。

1.1 静态多态(编译时多态)

静态多态是在编译时确定具体调用的函数,主要通过以下几种方式实现:
**函数重载:**同一作用域内,函数名相同但参数列表(参数类型、个数、顺序)不同的函数,编译器会根据实参匹配对应的函数。

复制代码
void print(int a) { cout << "int: " << a << endl; }
void print(double b) { cout << "double: " << b << endl; }
void print(string s) { cout << "string: " << s << endl; }

int main() {
    print(10);    // 调用print(int)
    print(3.14);  // 调用print(double)
    print("hello"); // 调用print(string)
    return 0;
}

编译器在编译时通过实参的静态类型,匹配最符合的函数,生成直接调用该函数的代码(静态绑定)。
运算符重载:对内置运算符(如+、<<、[ ] 等)进行重新定义,使其能用于自定义类型(如类对象),编译器会根据操作数类型匹配对应的重载版本。

复制代码
class Student
{
public:
	string name = "LING";

	//类对象与整数相加
	int operator+ (int m)
	{
		return this->age + m;
	}
	
private:
	int age = 18;
};

int main()
{
	Student obj1;

	int val = obj1 + 5; // int val = obj1.operator+ (5)
	cout << "类对象与整数相加: " << val << endl;

	return 0;
}

编译器将obj1 + 5转换为 obj1.operator+ (5),并根据操作数的静态类型匹配对应的运算符重载函数(静态绑定)。
**模版:**通过类型参数定义通用的函数或类,编译器根据实际传入的类型参数,在编译时生成具体的函数/类的实例,从而实现对不同类型的通用操作。

函数模版

复制代码
// 通用交换函数模板
template <typename T>
void swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int x=1, y=2;
    swap(x, y); // 编译时实例化 swap<int>(int&, int&)
    
    double a=3.14, b=6.28;
    swap(a, b); // 编译时实例化 swap<double>(double&, double&)
    return 0;
}

类模版

复制代码
// 通用的"加法器"类模板
template <typename T>
class Adder {
public:
    T add(T a, T b) { return a + b; } // 依赖T支持+运算符
};

int main() {
    Adder<int> intAdder;
    cout << intAdder.add(1, 2) << endl; // 3(int的+)
    
    Adder<string> strAdder;
    cout << strAdder.add("hello", "world") << endl; // "helloworld"(string的+)
    return 0;
}

模版是"代码生成器",编译器根据传入的类型参数,生成针对该类型的具体函数或具体类的代码。由于操作在编译时确定,因此属于静态多态。

1.2 动态多态(运行时多态)

动态多态是在程序运行阶段 才确定具体调用的函数,主要通过虚函数和继承实现。
实现动态多态必须满足以下3个条件:

    1. **存在继承关系:**有基类和派生类;
    1. 派生类重写基类的虚函数:派生类的函数与基类虚函数的函数名、参数列表、返回值完全相同(协变返回类型除外);
    1. 通过基类指针或引用调用虚函数:只有通过基类的指针或引用,才能触发运行时的函数绑定。
      **虚函数:**基类中用virtual关键字声明的函数就是虚函数,用于被子类重写。

**重写(Override):**派生类对基类虚函数的重新实现。
注:派生类重写时可以省略virtual,但最好加上,提高可读性。
多态的核心是动态绑定,当通过基类指针/引用调用虚函数时,函数调用不再根据对象的静态类型调用,而是由其实际指向的动态类型决定("指向谁,调用谁")。

复制代码
class Person 
{
public:
	virtual void BuyTicket() //虚函数 
	{ cout << "买票-全价" << endl; }
};

class Student : public Person 
{
public:
	virtual void BuyTicket() //重写基类虚函数
	{ cout << "买票-打折" << endl; }
};

void Func(Person* ptr)
{
	ptr->BuyTicket();
}

int main()
{
	Person ps;
	Student st;
	Func(&ps);
	Func(&st);

	return 0;
}

class Animal
{
public:
	virtual void talk() const //虚函数
	{}
};

class Dog : public Animal
{
public:
	virtual void talk() const //重写基类虚函数
	{
		std::cout << "汪汪" << std::endl;
	}
};

class Cat : public Animal
{
public:
	virtual void talk() const //重写基类虚函数
	{
		std::cout << "(>^ω^<)喵" << std::endl;
	}
};

void letsHear(const Animal& animal)
{
	animal.talk();
}

int main()
{
	Cat cat;
	Dog dog;
	letsHear(cat);
	letsHear(dog);
	return 0;
}

2. override和final

在C++中,虚函数重写(Override)的规则较为严格(需函数名、参数列表、返回值完全一致,协变返回除外),但手动保证"正确重写"容易出错。C++11引入override和final关键字,用于增强虚函数重写的安全性与明确性。

2.1 override:显式检测虚函数重写

告诉编译器"当前函数是重写基类的虚函数"。编译期检查时,若该函数并未真正重写基类虚函数(如函数名拼写错误、参数类型/个数不匹配),编译器会直接报错,避免运行时才发现错误。

复制代码
class Base 
{
public:
    virtual void func(int x) { /* ... */ }
};

class Derived : public Base 
{
public:
    // 编译报错:Base中没有"virtual void func(double x)"的虚函数可重写
    void func(double x) override { /* ... */ }
};

2.2 final:禁止重写或继承

final有两种用法:

(1)禁止该类被继承(已经在前面继承章节讲到,这里就不提了)。

(2)禁止派生类重写该虚函数函数。

复制代码
class Base 
{
public:
    // final标记:该虚函数不能被派生类重写
    virtual void func() final { /* ... */ }
};

class Derived : public Base 
{
public:
    // 编译报错:Base::func() 已被final禁止重写
    void func() override { /* ... */ }
};

3. 多态的原理(vptr、vtable)

C++多态的底层实现依赖虚函数表 (Virtual Table,简称vtable)和虚表指针(Virtual Pointer,简称vptr),核心是运行时动态绑定。

虚函数表(vtable):类级别的函数地址"索引表"

**本质:**每个包含虚函数的类,编译器会为其生成一张虚函数表(本质是"函数指针数组"),用于存储该类所有虚函数的地址。
特点:

  • 每个包含虚函数的类只有一张虚函数表(所有同类型的对象共享同一张虚函数表),在编译时生成,存储在全局数据区。
  • 若派生类重写了基类的虚函数,派生类的虚函数表中,对应基类虚函数 的位置会被覆盖为派生类重写后的函数地址;
  • 若派生类有新增虚函数,则这些函数的地址会被追加到派生类虚函数表的末尾。

虚表指针(vptr):对象级别的表指针,对象与虚函数表的"纽带"

本质: 每个包含虚函数的类对象中,会隐含一个指向"当前对象所属类的虚函数表指针"(通常位于对象内存的起始位置,由编译器自动添加)。
特点: 每个包含虚函数的类的对象的虚表指针vptr,在对象创建时被初始化,指向该对象所属类的虚函数表。

示例1

复制代码
class Person 
{
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
private:
	string _name;
};

class Student : public Person 
{
public:
	virtual void BuyTicket() //重写虚函数BuyTicket
	{ cout << "买票-打折" << endl; }
private:
	string _id;
};

class Soldier : public Person 
{
public:
	virtual void BuyTicket() //重写虚函数BuyTicket
	{ cout << "买票-优先" << endl; }
private:
	string _codename;
};
void Func(Person* ptr)
{
	ptr->BuyTicket();
}

int main()
{
	Person ps;
	Student st;
	Soldier sr;

	Func(&ps);
	Func(&st);
	Func(&sr);

	return 0;
}

派生类Student和Soldier继承Person后,会生成各自的虚函数表 ,由于Student和Soldier重写了BuyTicket,它们的虚函数表中,BuyTicket的地址会被替换为自身的Student: :BuyTicket或Soldier: :BuyTicket的地址;

包含虚函数的类创建对象时,对象的vptr会被初始化为指向所属类的虚函数表(如创建Person ps 时, ps的vptr会被初始化为指向Person类的虚函数表)

调试观察:

多态调用完整流程(以调用Func(&st) ,最终输出买票--打折为例):

**编译时:**编译器发现BuyTicket是虚函数,且通过指针ptr调用,会触发动态绑定,因此不会直接确定函数地址,而是生成"通过vptr查找vtable"的代码。

运行时:

  1. 通过指针ptr找到它所指向的Student对象(动态类型对应的对象);
  2. 从Student对象中取出vptr;
  3. 通过vptr找到Student类的vtable,在表中查找BuyTicket()对应的函数地址(此时地址是Student::func()的地址,因为派生类重写后覆盖基类版本)。
  4. 调用找到的函数地址,最终执行Student::func(),实现多态。
    这就是多态的底层实现:通过虚函数表和虚指针,在运行时根据对象实际类型决定调用哪个函数。

示例2

复制代码
class Base 
{
public:
    virtual void func1() { cout << "Base::func1" << endl; }
    virtual void func2() { cout << "Base::func2" << endl; }
};

class Derived : public Base 
{
public:

    void func1() override { cout << "Derived::func1" << endl; } // 重写func1
    virtual void func3() { cout << "Derived::func3" << endl; } // 新增虚函数func3    
};

int main()
{
    Base b;
    Derived d;
    Base* ptr = new Derived();
    ptr->func1();

    return 0;
}

调试观察:

基类Base的虚函数表结构(声明了两个虚函数):

  • 0\]:Base::func1()的函数地址。

派生类Derived的虚函数表结构(Derived继承了Base,并重写了func1,新增了虚函数func3()):

  • 0\]:Derived::func1()的函数地址(重写后,覆盖了基类Base::func1()的地址)。

  • 2\]:Derived::func3()的函数地址(新增虚函数,追加到表的末尾,这里调试中观察不到)。

  • 编译时:编译器根据指针类型Base*,检查Base中是否有func1(存在且是虚函数),但不决定具体调用哪个版本。

  • 运行时:

  1. 通过ptr找到Derived对象的虚表指针(__vfptr);

  2. 由__vfptr找到Derived类的虚函数表;

  3. 在表中找到func1的地址(因派生类重写,此处是Derived::func1()的地址);

  4. 调用该地址的函数,实现"运行时多态"。

结论:

派生类重写基类虚函数时,虚函数表中对应位置的地址会被修改为派生类的实现 (如Derived::func1覆盖Base::func1)。

未重写的虚函数,派生类虚函数保留基类的函数地址 (如Derived::vtable[1]仍为Base::func2)。

派生类如果有新增的虚函数,这些虚函数的地址会被添加到派生类虚函数表的末尾(调试中观察不到,逻辑上func3对应[2])。

4. 含虚函数的对象的大小

下面在32位程序下的运行结果是什么()

A. 编译报错 B. 运行报错 C. 8 D. 12

复制代码
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
protected:
	int _b = 1;
	char _ch = 'x';
};
int main()
{
	Base b;
	cout << sizeof(b) << endl;
	return 0;
}

含有虚函数的类,其对象会包含一个虚表指针(vptr),用于指向类的虚函数表(vtable)。在32位程序中,指针的大小为4字节。int是4字节,char是1字节,为满足内存对齐补3个字节。计算:vptr(4)+int(4)+char(1)+填充(3)=12字节。

5. 动态绑定与静态绑定

静态类型: 变量或表达式在编译阶段就确定的类型,由声明时的类型直接决定,不会随程序运行而改变。

动态类型: 变量或表达式在运行阶段实际指向的对象类型,即"真实的对象类型",运行时确定,可能随程序运行而改变(例如指针重新指向不同类型的对象)。

复制代码
class Base {};
class Derived : public Base {};

int main() {
    Base b;         // 静态类型:Base(声明时确定)
    Derived d;      // 静态类型:Derived(声明时确定)
    Base* ptr = &d; // 静态类型:Base*(指针声明为Base*,与指向的对象无关)动态类型:Derived*(实际指向Derived对象)
    ptr = &b;       // 动态类型变为Base* (重新指向Base对象)
    Base& ref = d;  // 静态类型:Base&(引用声明为Base&,与引用的对象无关)动态类型:固定为 Derived&(实际引用Derived对象)

    return 0;
}

静态绑定

对于非虚函数,编译器是根据静态类型来调用的。

规则:编译器根据变量的静态类型 确定调用哪个函数,在编译时就已确定调用关系。

复制代码
class Base {
public:
    void func() { cout << "Base::func()" << endl; } // 非虚函数
};
class Derived : public Base {
public:
    void func() { cout << "Derived::func()" << endl; } // 非虚函数(隐藏基类版本)
};

int main() {
    Base* ptr = new Derived(); 
    ptr->func(); // 调用哪个?
    return 0;
}

ptr的静态类型是Base*,非虚函数func采用静态绑定。编译时,编译器直接根据静态类型Base*确定调用Base::func(),输出Base::func()。

动态绑定

对于虚函数,编译器是根据动态类型来调用的。

规则:编译器根据变量的动态类型确定调用哪个函数,在运行时才确定调用关系。

复制代码
class Base {
public:
    virtual void func() { cout << "Base::func()" << endl; } // 虚函数
};
class Derived : public Base {
public:
    void func() override { cout << "Derived::func()" << endl; } // 重写虚函数
};

int main() {
    Base* ptr = new Derived(); 
    ptr->func(); // 调用哪个?
    return 0;
}

ptr的静态类型是Base*,当动态类型是Derived*,虚函数采用动态绑定。运行时,程序通过ptr指向的Derived对象的虚表指针,找到Derived的虚函数表,调用Derived::func(),输出Derived::func()。

编译器的执行过程:

编译时:

第一步:检查被调用的函数是否为虚函数

  • 若函数不是虚函数,直接采用静态绑定 。编译器根据调用者的静态类型,在编译阶段就确定要调用的函数地址,生成"直接调用该地址函数"的机器码。
  • 若是虚函数:进行下一步,检查调用方式。

第二步:检查调用方式

  • 如果通过对象本身调用(如d.func(),d是Derived类型的对象),对象的静态类型与动态类型完全一致,生成"直接调用该地址函数"的机器码 (本质是静态绑定)。
  • 如果通过基类的指针或引用调用(如ptr->func(),ptr是Base*类型),编译器无法在编译时确定指针/引用的动态类型(可能指向Base或Derived),因此会生成动态绑定调用逻辑的机器码(即①从指针/引用指向的对象中取出vptr②通过vptr找到所属类的vtable③在vtable中找到目标函数的地址④调用该函数)

运行时:

  • **静态绑定:**编译时生成的机器码是"直接跳转至固定函数地址"的指令(例如call 0x123456),运行时,CPU直接执行这条命令,跳转到编译时确定的函数地址。
  • **动态绑定:**编译时生成的机器码是"通过vptr查找vtable并获取函数地址"的逻辑,运行时,执行机器码,最终调用的函数地址由运行时的vtable决定。

ptr是指针+func()是虚函数满足多态条件,实现动态绑定,汇编层:

func()不是虚函数,不满足多态条件,实现静态绑定,编译器直接确定调用函数地址,汇编层:

6. 经典面试题

下面程序的输出结果是什么()

A. A->0 B. B->1 C. A->1 D. B->0 E. 编译出错 D. 以上都不正确

复制代码
class A
{
public:
	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
	virtual void test() { func(); }
};

class B : public A
{
public:
	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
};

int main(int argc, char* argv[])
{
	B* p = new B;
	p->test();

    //p->func(); 输出B->0
	
	return 0;
}

重写的核心规则:"签名一致,替换实现",函数重写实际上只重写了函数体,没有重写函数声明中的参数。
函数的参数没有被重写的原因:

**接口一致性:**重写的目的是"改变函数的行为",而函数的参数规则(包括默认参数)被视为"接口规范"的一部分,应由基类定义(避免派生类随意修改参数规则导致接口混乱)。

**效率:**默认参数在编译时即可确定,无需像函数体那样动态查找,否则会增加运行时开销。

解析这道题之前,我们需要了解的规则:

在C++中,如果函数的所有参数都指定了默认值,那么当遇到无参调用时,编译器会在编译阶段自动补充默认参数。

复制代码
void func(int a=1) {}
func()//编译器自动补全func(1)

void func(int a = 1, int b = 2) {}
func()//编译器自动补全func(1,2)

编译器在编译阶段就会把 "无参调用" 替换成 "带默认值的调用"。
默认参数遵循静态绑定规则,若调用时未传参,默认参数是编译器在编译时,根据 "调用者的静态类型" 从类声明中直接读取的。

this指针的静态类型,等于函数所属的类的类型;this指针的动态类型,等于调用该函数的实际对象类型。

解析:

p->test():

  • test()是虚函数,p动态类型是B*--->根据动态类型B的vptr找到vatble--->由于B类没有重写test(),所以vatble中的test()指针还是指向A::test()
  • A::test()内this->func()--->this的静态类型是A*(test是A类的成员函数)--->所以默认参数采用A::func的val=1---> this的动态类型是B*--->func是虚函数,this的动态类型是B*,根据动态类型找到B类重写的func()函数--->执行B::func(1)--->输出B->1。

p->func():

  • p的静态类型是B*--->编译时采用val=0 补全B的默认参数--->func是虚函数,p的动态类型是B*--->根据动态类型找到B类重写的func()函数---> 执行B::func(0)--->输出B->0。
    总结:虚函数的实现看实际对象类型(动态绑定),默认参数看编译时静态类型(静态绑定)。

7. 析构函数的重写

为什么基类中的析构函数建议设计成虚函数呢?

复制代码
//基类
class Base 
{
public:
    Base() { cout << "Base 构造" << endl; }
 
    // 非虚析构(错误示范)
    // ~Base() { cout << "Base 析构" << endl; }
 
    // 虚析构(正确做法)
    virtual ~Base() { cout << "Base 析构" << endl; }
};
 
//派生类
class Derived : public Base  
{
private:
    int* data;   //派生类动态资源
public:
    Derived() : data(new int(10)) { cout << "Derived 构造" << endl; }
    ~Derived() //派生类析构:先释放自身资源,再调用基类析构
    {
        delete data;  //释放派生类资源
        cout << "Derived 析构" << endl;
    }
};
 
int main() 
{
    Base* ptr = new Derived(); //基类指针指向派生类对象
    delete ptr;   //销毁对象
    return 0;
}

当我们通过delete ptr销毁对象时,程序的行为取决于基类析构函数是否为虚函数:

若~Base()是非虚函数 :析构函数调用采用"静态绑定",根据静态类型Base*,只调用~Base(),**派生类的~Derived()未被调用,导致data指向的动态内存未被释放,造成内存泄漏。**运行结果:

若~Base()是虚函数:满足动态多态,析构函数调用采用"动态绑定",根据动态类型Derived*,先调用~Derived()释放派生类资源,再调用~Base()释放基类资源,符合预期。运行结果:


为了让析构函数支持多态(从而保证资源被正确释放),编译器必须特殊处理析构函数的名称,使其满足"虚函数重写"的条件。

基类析构函数名称和派生类析构函数名称不可能相同,**所以析构函数的名称被统一处理为destructor,**为了让析构函数满足"虚函数重写"的要求,从而支持多态,保证对象能按"派生类->基类"的顺序正确析构,避免资源泄漏。
所以当基类析构函数不是虚函数时,派生类析构函数与基类析构函数因编译后名称统一为destructor,会构成"函数隐藏"关系(继承章节讲到)。
派生类析构无需显式virtual,只要基类析构是虚函数,派生类析构自动为虚函数。
如果一个类明确不会被继承,或不会通过基类指针/引用销毁派生类对象,那么基类析构函数可以不是虚函数,但在实际开发中,往往需要考虑"可继承性",所以建议基类析构函数设计为虚函数。

8. 协变

在C++中,协变是虚函数重写的一种特殊规则,当基类函数返回基类的指针或引用时,派生类重写该虚函数时,可以返回派生类的指针或引用,这种情况称为协变返回类型。

协变的条件:

  • 基类虚函数的返回值必须是基类类型的指针或引用;
  • 派生类重写的返回值必须是派生类类型的指针或引用;
复制代码
class Base 
{
public:
    // 基类虚函数,返回 Base*
    virtual Base* clone() const 
    {
        return new Base(*this); // 复制自身(基类对象)
    }
    virtual ~Base() {} // 虚析构,避免内存泄漏
};

class Derived : public Base 
{
public:
    // 派生类重写 clone,返回 Derived*(协变)
    Derived* clone() const override 
    {
        return new Derived(*this); // 复制自身(派生类对象)
    }
};

int main() 
{
    Base* b = new Derived();    
    Base* bClone = b->clone(); //调用 Derived::clone(),返回 Derived*(协变允许),隐式转换为 Base*
    delete bClone;
    delete b;
    return 0;
}

9. 纯虚函数和抽象类

纯虚函数

纯虚函数是基类中声明的特殊虚函数它没有具体的函数体 ,通过在虚函数声明末尾添加**=0**标识(纯虚函数的"=0"表示"无实现")。

**作用:**纯虚函数的核心作用是强制派生类必须实现该函数,从而实现多态。

抽象类

包含纯虚函数的类称为抽象类。

抽象类的特点:

  • 抽象类不能创建对象,仅作为接口基类。
  • 派生类必须重写所有虚函数才能实例化,否则派生类也是抽象类。
复制代码
// 抽象类:包含纯虚函数draw()
class Shape {
public:
    // 纯虚函数:强制派生类实现绘制逻辑
    virtual void draw() = 0;

    // 普通虚函数(可选):可提供默认实现
    virtual void printInfo() {
        cout << "This is a shape." << endl;
    }

    // 析构函数通常声明为虚函数(避免多态销毁时内存泄漏)
    virtual ~Shape() {}
};

// 派生类:Circle,必须重写draw()
class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}

    // 重写纯虚函数draw()
    void draw() override {
        cout << "绘制一个半径为" << radius << "的圆" << endl;
    }

    // 重写普通虚函数(可选)
    void printInfo() override {
        cout << "这是一个圆,半径:" << radius << endl;
    }
};

// 派生类:Rectangle,必须重写draw()
class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}

    void draw() override {
        cout << "绘制一个宽" << width << "、高" << height << "的矩形" << endl;
    }
};

int main() {
    // 错误:抽象类不能实例化
    // Shape shape; 

    // 正确:通过派生类实例化
    Shape* shapes[2] = {
        new Circle(5.0),
        new Rectangle(3.0, 4.0)
    };

    // 多态调用:根据动态类型执行对应派生类的draw()
    for (int i = 0; i < 2; i++) {
        shapes[i]->draw();
        shapes[i]->printInfo();
        delete shapes[i];
    }

    return 0;
}

纯虚函数与普通虚函数的区别:

  • 普通虚函数有默认实现,派生类可以选择重写或继承。
  • 纯虚函数无默认实现,派生类必须重写

10. 虚函数表末尾的标记(了解)

C++ 标准从未规定虚函数表的具体结构(包括是否需要结束标记),这属于编译器的 "未定义行为",不同厂商的实现可以不同:

  • VS系列编译器:会在 vtable 末尾添加一个 NULL指针0x00000000作为结束标记。
  • GCC(g++)系列编译器:通常不添加结束标记

VS可自行调试观察虚函数表的末尾。

相关推荐
ceclar1235 小时前
C++Lambda表达式
开发语言·c++·算法
bkspiderx5 小时前
Linux网络与路由配置完全指南
linux·运维·网络·c++
慧都小妮子5 小时前
基于C++ UA Server SDK开发高性能与跨平台 OPC UA 服务器
c++·跨平台·高性能·opc ua·ua server sdk
INGNIGHT6 小时前
单词搜索 II · Word Search II
数据结构·c++·算法
楼田莉子6 小时前
C++学习:C++11关于类型的处理
开发语言·c++·后端·学习
彷徨而立7 小时前
【C/C++】只知道窗口句柄,如何擦除窗口内容,清理窗口?
c语言·c++·windows
強云7 小时前
匿名命名空间 - c++
c++
云知谷7 小时前
【经典书籍】C++ Primer 第14类虚函数与多态精华讲解
c语言·开发语言·c++·软件工程·团队开发
HVACoder7 小时前
复习下线性代数,使用向量平移拼接两段线
c++·线性代数·算法