文章目录

命令空间

命明空间的两种使用方式及自定义命名空间

1.1using指令申明

例如

using namespace std;

将整个命名空间引入当前作用域,后续可直接使用cout等名称

1.2作用域限定符(::)直接访问

  1. 概念::: , 用于指定一个成员(如函数或变量)属于特定的类或命名空间,例如,在类的外部定义成员函数时,::用于指定该函数属于那个类

  2. 此处是用于指定属于哪个命名空间

std::cout << "Hello World!" << std::endl;

  1. 此处是用于指定属于那个类
void host::info(string newName,string  newNum) //指定这个函数属于host类
{
    name=newName;
    num=newNum;
}

2.自定义命名空间

例如

namespace add{
    int add(int a,int b){
        return a+b;
    }
}

输入输出

尖括号指向谁,表示数据要往哪里去

1.标准输出流(cout)

类似于C语言的printf

#include <iostream>

using namespace std;

int main()
{
    int a=1;
    int b=2;
    cout << "Hello World!" << endl; 
    cout <<a<<"+"<<b<<"="<<a+b<<endl; 
    return 0;
}

cout << “Hello World!” << endl;
相当于c语言中的printf(“Hello World!\n”);
cout <<a<<“+”<<b<<“=”<<a+b<<endl; c++中的输出拼接
相当于C语言中的printf(“%d+%d=%d\n”,a,b,a+b);

运行结果
在这里插入图片描述

2.标准输入流(cin)

类似于C语言中的scanf

#include <iostream>

using namespace std;

int main()
{
    int a=0;
    int b=0;
    cin>>a;
    cin>>b;
    cout << "Hello World!" << endl;
    cout <<a<<"+"<<b<<"="<<a+b<<endl;
    return 0;
}

运行结果
在这里插入图片描述

宽字符

1.概述

wchar_t 是宽字符类型,每个字符占 2 或 4 个字节(取决于平台,通常 Windows 上是 2 字节 wchar_t,Linux 上是 4 字节)。

2.用法示例

#include <iostream>
#include <wchar.h>

using namespace std;

int main()
{

    wchar_t hello[]=L"Hello World!";
    wcout << hello << endl;

    return 0;
}

注解

在 C/C++ 中,L 是一个字符串字面量的前缀,用来表示宽字符字符串

没有 L:"Hello" 是普通窄字符串,每个字符占 1 个字节(char 类型)。
加上 L:L"Hello" 是宽字符串,每个字符占 2 或 4 个字节(取决于平台,通常 Windows 上是 2 字节 wchar_t,Linux 上是 4 字节)。

配合wcout使用

窄字符(char / string)→ 用 std::cout
宽字符(wchar_t / wstring)→ 用 std::wcout)

运行结果
在这里插入图片描述

内联函数

1.概述

内联函数是 C++ 中的一种优化机制,通过 inline 关键字建议编译器在调用处直接展开函数代码,以减少函数调用的开销(压栈、跳转、返回等)

2.特点

  1. 语法
inline int add(int a, int b)
{
    return a+b;
}
  1. 作用
    消除函数调用的开销(参数传递、压栈、跳转、返回等),对于短小、频繁调用的函数效果明显。

    对比项 普通函数 内联函数
    执行过程 跳转到函数体 → 执行 → 跳回 直接执行复制出来的代码
  2. 本质:建议而非命令
    inline 只是向编译器提建议,编译器决策,即使函数被声明为内联函数,编译器即也可决定不执行内联(特别是函数体过大、递归函数等)。现代编译器会根据优化级别自动内联合适的函数,即便没有 inline 关键字。

  3. 链接规则
    内联函数的定义(而非仅仅是声明)必须在每个调用它的源文件中都可见(通常定义在头文件中)。

4.注意事项

过度使用
过度使用内联会导致可执行文件体积增大,反而降低缓存命中率

编译器决定
最终是否使用函数内联由编译器决定,即使函数被声明为内联函数

什么时候使用
适合:函数体极短、调用极其频繁
不适合:函数体复杂、递归函数等

5.用法示例

#include <iostream>

using namespace std;
inline int add(int a, int b)
{
    return a+b;
}
int main()
{
    int c=add(2,4);//编译器可能会将此优化为 int c =2+4;
    cout <<c << endl;

    return 0;
}

运行结果
在这里插入图片描述

Lambda

1.概述

它是 C++11 引入的一种匿名函数对象的写法,非常适合用来写短小、临时的可调用代码

2.语法

[capture clause](parameters) -> return_type { body }
  • 捕获列表[capture clause]:用于捕获外部变量,在Lambda表达试中可以访问这些变量
  • 参数列表(parameters) :与普通函数参数列表类似
  • 返回类型(return_type):auto 推导出 lambda 的匿名函数对象类型,指定返回类型->return_type,如果函数只有一条返回语句,可以省略返回类型
  • 函数体(body):Lambda表达式的函数体,包含要执行的代码

3.捕获方式

1.[ ]

不捕获任何外部变量

#include <iostream>

using namespace std;

int main()
{
    int x=1;
    int y=1;
    auto a=[](int x, int y)->int{

        return x+y;
    };
    int b=a(x,y);
    cout <<b<< endl;

    return 0;
}

运行结果
在这里插入图片描述

2.[ x,y]

捕获外部变量x、y,不能修改变量,类似只读

#include <iostream>

using namespace std;

int main()
{
    int x=1;
    int y=2;
    auto a=[x,y]()->int{
        return x+y;
    };
    int b=a();
    cout <<b<< endl;

    return 0;
}

运行结果
在这里插入图片描述

3.[=]

捕获全部外部变量,不能修改变量,类似只读

#include <iostream>

using namespace std;

int main()
{
    int x=5;
    int y=2;
    auto a=[=]()->int{
        return x+y;
    };
    int b=a();
    cout <<b<< endl;

    return 0;
}

运行结果
在这里插入图片描述

4.[&]

引用的方式,捕获所有变量,可以修改变量,类似指针,可读可写

#include <iostream>

using namespace std;

int main()
{
    int x=5;
    int y=2;
    auto a=[&]()->int{
         x=9;  //引用捕获方式,可以修改变量
        return x+y;
    };
    int b=a();
    cout <<b<< endl;

    return 0;
}

运行结果
在这里插入图片描述

4.用法示例

常见用法

#include <iostream>

using namespace std;

int getmin(int a ,int b,bool (*p)(int a,int b))
{
    if(p(a,b))
        {
            return b;
        }
    else
        {
           return a;
        }
}
int main()
{
    int x=1;
    int y=2;				//仅当无捕获时,可隐式转换为同名函数指针
    int min=getmin(x,y,[](int a,int b)->bool{
        return a>b;
    });
    cout <<min<< endl;

    return 0;
}

运行结果
在这里插入图片描述

1.概述

类的概念是面向对象编程的核心之一,其主要目的是将数据和与数据相关的操
作封装在一起。
C++类的基本结构通常包含:

1.数据成员(Attributes):定义类的属性。这些是类内部的变量,用于存储对象的状态。
2.成员函数(Methods):定义类的行为。这些是可以操作对象的数据成员的函数。
3.构造函数和析构函数:特殊的成员函数。构造函数在创建对象时自动调用,用于初始化对象。析构函数在对象销毁时调用,用于执行清理操作。
4.访问修饰符:如public,private,protected,用于控制对类成员的访问权限。例如,public
成员可以在类的外部访问,而private成员仅类内部(及友元)可访问,默认权限。protected:类内部和派生类可访问。
5.继承:允许一个类继承另一个类的特性。这是代码重用和多态性的关键。

2.访问权限

访问权限 类内部 同一个类的对象(类的实例) 派生类(子类) 类外部
public ✔可访问 ✔可访问 ✔可访问 ✔可访问
private ✔可访问 ❌不可访问 ❌不可访问 ❌不可访问
protected ✔可访问 ❌不可访问 ✔可访问 ❌不可访问

3.代码示例

一般类的定义在头文件实现,具体成员函数在源文件实现

#include <iostream>
#include <string>
using namespace std;

class host{
    string name;
    string num;
public:
  void info(string newName,string newNum);
  void prinfo();
};
class dog {
    string name;
    string sounds;
    host* host1;  //类的组合,狗有主人,这个主人可以是一个类
  public:   //公有成员函数作为接口,用于外部设置或获取私有成员变量
    void info(string newName,string  newSounds,host* newHost); //成员函数声明
    void prinfo();
};
void host::info(string newName,string  newNum)  //成员函数,类外部实现
{
    name=newName;
    num=newNum;
}
void host::prinfo()
{
    cout <<"主人的名字:"<<name<<endl;
    cout<<"主人的电话:"<<num<<endl;
}
void dog::info(string newName,string  newSounds,host* newHost)
{
    name=newName;
    sounds=newSounds;
    host1=newHost;
}
void dog::prinfo()
{
    cout <<"狗的名字:"<<name<<endl;
    cout<<"狗的叫声:"<<sounds<<endl;
    host1->prinfo();
}
int main()
{
    dog huahua;
    host* maomao= new host;     //动态创建host对象, 创建(分配+构造)

    maomao->info("猫猫","123456");
    huahua.info("花花","汪汪",maomao);
    huahua.prinfo();
    delete maomao;	// 释放(析构+回收内存)
    return 0;
}

运行结果
在这里插入图片描述

引用

1.概述

引用:是已存在变量的别名。就像一个人的“绰号”和“本名”指向同一个人。

2.引用和指针的三个主要区别

特性 引用(&) 指针(*)
初始化 必须在定义时初始化,且绑定到一个对象 可以在任何时间被初始化,定义时可以不初始化,之后赋值
重新绑定 不可改变绑定的对象,始终指向初始对象 可以改变指向,重新赋值指向另一个对象
是否可为空 不能为空,必须引用一个有效对象 可以为 NULL

3.代码示例

1.将引用作为参数
#include <iostream>
#include <string>
using namespace std;

void swap(int& ra,int& rb)
{
    int temp=ra;
    ra=rb;
    rb=temp;
}

int main()
{
    int a=5;
    int b=6;

    cout<<"交换前"<<endl;
    cout<<"a:"<<a<<endl<<"b:"<<b<<endl;

    swap(a,b);
    cout<<"交换后"<<endl;
    cout<<"a:"<<a<<endl<<"b:"<<b<<endl;
    return 0;
}


运行结果

在这里插入图片描述

2.将引用作为返回值

当返回一个引用时,要注意被引用的对象的生命周期,被引用的对象必须在函数返回后依然存在。 例如:局部变量:函数结束即销毁 → 不能返回其引用

#include <iostream>
#include <string>
using namespace std;

int val[3]={1,2,3};
int& setval(int i)
{
    int& ret=val[i];
    return ret;
}

int main()
{

    setval(1)=66;
    cout<<"val[1]:"<<val[1]<<endl;
    return 0;
}

运行结果
在这里插入图片描述

重载

1.概述

定义:在同一作用域内,可以定义多个同名但参数列表不同的函数或运算符

2.函数的重载

  • 参数个数、类型或顺序不同
  • 返回值类型不能作为区分依据

代码示例

#include <iostream>
#include <string>
using namespace std;

void print(int x) { cout << "int: " << x<<endl; }
void print(double x) { cout << "double: " << x<<endl; }
void print(string s) { cout << "string: " << s<<endl; }

int main()
{

    print(6);
    print(9.8);
    print("hello");
    return 0;
}

运行结果
在这里插入图片描述

3.运算符重载

1.概述
  • 为已有运算符(如 +, -, [])赋予自定义类型的新含义
  • operator 是一个关键字,后面紧跟一个运算符符号(如 +、-、=、[] 等),例如:组合起来 operator+ 就代表“加法运算符函数”。
2.代码示例
#include <iostream>
#include <string>
using namespace std;

class prit
{
    public:
        int x,y;
        prit operator+(prit temp);
};
prit prit::operator+(prit temp)
{
    prit ret;
    ret.x=x+temp.x;
    ret.y=y+temp.y;
    return ret;
}
int main()
{

   prit p1;
   p1.x=1;
   p1.y=2;
   prit p2;
   p2.x =1;
   p2.y=2;
   prit p3=p1+p2; //p1被作为调用对象,p2被作为参数传入,等价于 p1.operator+(p2)
   cout <<"p3.x:"<<p3.x<<endl;
   cout<<"p3.y:"<<p3.y<<endl;

    return 0;
}

运行结果
在这里插入图片描述

补充说明

这里参数temp是通过值传递,也可以用const prit,这样避免了拷贝构造的开销,又保证了加法运算符不会无意改变右操作数的值

prit operator+(const prit& other) const;,后面这个const,可以确保某些函数不会意外的改变对象的状态,还可以使得这个函数可以在在常量对象被调用
为什么可以使得常量对象被调用?

  • 在 C++ 中,常量对象(const 对象)只能调用被声明为 const 的成员函数。

为什么常量对象只能调用const成员函数?

  • 常量对象意味着它的成员变量不能被修改。
  • 如果调用一个非 const 成员函数,编译器无法保证该函数不会修改成员变量(即使它实际上没有修改,编译器也会拒绝),因此会直接报错,防止潜在的修改。而 const 成员函数承诺不会修改对象状态,编译器允许常量对象调用它。
4.注意事项
  • 一致性:重载后的运算符行为应与内置类型或常规语义一致。例如,+运算符通常应该实现加法,而不是其他意外的操作
  • 复杂性:过度使用会让代码难以理解和维护

构造函数

1.概述

构造函数是类的一种特殊成员函数,在创建对象时自动调用执行,构造成员变量的初始化值、内存空间
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回void。构造函数可用于为某些成员变量设置初始值。

2无参构造函数

默认为无参构造函数,如果用户没有定义任何构造函数,编译器会自动生成一个空的无参构造函数(但不会初始化内置类型成员)。

代码示例

#include <iostream>
#include <string>
using namespace std;

class dog
{
    public:
        string name;
        dog(){
            cout<<"无参构造函数被调用"<<endl;
        }
};

int main()
{
    dog xiaohuang;
    return 0;
}

运行结果
在这里插入图片描述

3.带参数构造函数

代码示例

#include <iostream>
#include <string>
using namespace std;

class dog
{
    public:
        string name;
        dog(string name1){  //带参数构造函数
            name=name1;
            cout<<name<<endl;
        }
};

int main()
{
    dog hua("小花");
    return 0;
}

运行结果
在这里插入图片描述

4.参数列表的方式构造

1.概述

参数列表的方式构造指的是 C++ 构造函数中的初始化列表语法,用于在进入构造函数体之前初始化成员变量。

基本语法
class 类名 { public: 类名(参数列表) : 成员1(值1), 成员2(值2), ... { // 构造函数体 } };
为什么用初始化列表?

原因 说明
效率 直接初始化成员,避免先默认构造再赋值
必要性 有些成员必须在初始化列表中初始化:const 成员、引用成员、没有默认构造函数的类成员、基类带参数的构造函数
顺序确定性 成员按声明顺序初始化,而不是按列表顺序
2.代码示例
#include <iostream>
#include <string>
using namespace std;

class dog
{
    public:
        string name;
        dog(string name1):name(name1){
            cout<<name<<endl;
        }
};

int main()
{
    dog huang("小黄");
    return 0;
}


运行结果
在这里插入图片描述

this关键字

1.概述

在C++中,this 关键字是一个指向调用对象的指针。它在成员函数内部使用,用于引用调用该函数的对象。使用 this 可以明确指出成员函数正在操作的是哪个

2.用法示例

#include <iostream>
#include <string>
using namespace std;

class dog
{
    public:
        string name;
        dog(string name){
            this->name=name; //使用this指针区分成员变量和参数
        }
        void print();

        dog& setname(string name);

};
void dog::print()
{
    cout<<this->name<<endl; //也可以不使用this->,直接写name
}
dog& dog::setname(string name)
{
    this->name=name;  //更新名字
    return *this;   //返回调用对象的引用
}
int main()
{

    dog huang("小黄");
    huang.print();
    //链式调用
    huang.setname("大黄").print();    //huang.setname("大黄"),返回调用对象的引用,即huang
    return 0;
}

运行结果
在这里插入图片描述

new关键字

1.概述

在 C++ 中,new 关键字用于动态分配内存。它是 C++ 处理动态内存分配的主要工具之一,允许在程序运行时根据需要分配内存

2.基本用法

1.分配单个对象

int* ptr = new int;   // 类似C语言中:int *p = (int *)malloc(sizeof(int));

2.分配对象数组

int* arr = new int[10];   //类似C语言中:int *arr = (int *)malloc(sizeof(int)*10);

3.创建类对象,对于单个对象,可以使用构造函数的参数

host* maomao= new host;
MyClass* obj = new MyClass(arg1, arg2);

4.与 delete 配对使用

  • 释放单个对象
		delete ptr;   // 释放 ptr 指向的对象
  • 释放对象数组
delete[] arr;   // 释放 arr 指向的数组
  • 释放类
delete maomao;             // 释放(析构+回收内存)

析构函数

1.概述

析构函数是C++中的一个特殊的成员函数,它在对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。析构函数特别重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。

2.基本特性

1.名称:析构函数的名称由波浪号(~) 后跟类名构成,如~Myc1ass()。
2.无返回值和参数:析构函数不接受任何参数,也不返回任何值。
3.自动调用:当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用delete删除一个动态分配的对象),析构函数会被自动调用。
4.不可重载:每个类只能有一个析构函数。
5.继承和多态:如果一个类是多态基类,其析构函数应该是虚的。

3.代码示例

#include <iostream>
#include <string>
using namespace std;


class dog
{
private:
    int* datas;
    public:
        string name;

        dog(string name,int size){
            this->name=name;
            this->datas=new int[size];
        }
        ~dog(){
            cout<<"解析函数被调用"<<endl;
            delete[] this->datas;
        }

};
int main()
{
    dog* hua=new dog("花",5);
    delete hua;//delete hua 会调用 dog 类的析构函数
    return 0;
}

补充说明

当用户没有定义析构函数时,编译器会生成一个默认析构函数

默认析构函数会
自动调用非静态成员对象(如 string name)的析构函数(正确释放 name 的内部资源)。
但不会释放普通指针成员(如 int* datas)所指向的内存。它只销毁指针变量本身(指针占用的栈内存),而指针指向的堆内存仍然存在,导致内存泄漏

运行结果
在这里插入图片描述

静态成员

1.概述

在 C++ 中,静态成员是归属于类本身而非某个具体对象的成员。无论创建多少个对象,静态成员在内存中只有一份副本,被所有对象共享。

2.静态成员变量

  • 在类内声明时使用 static 关键字。
  • 必须在类外单独定义并初始化
#include <iostream>
using namespace std;


class MyClass {
public:
    static int count;      // 声明静态成员变量

};

// 类外定义(不重复 static)
int MyClass::count = 0;
int main()
{
    MyClass obj;
    cout<<obj.count<<endl;
    cout<<MyClass::count<<endl;
    return 0;
}

访问方式

  • 通过类名:MyClass::count
  • 通过对象:obj.count

特点

  • 存储于静态存储区,生命周期贯穿整个程序。
  • 不占用对象内存,即 sizeof(MyClass) 不包含静态成员。
  • 访问控制(public/protected/private)同样生效

3.静态成员函数

  • 使用 static 关键字声明。
  • 只能访问静态成员(变量或其他函数),不能直接访问非静态成员
#include <iostream>
using namespace std;


class MyClass {
public:
    static int data;
    static void show() {
        cout << data<<endl;   // ✅ 可以访问静态成员
      }
};

int MyClass::data=1;
int main()
{
    MyClass obj;
    obj.show();
    MyClass::show();
    return 0;
}

调用方式: MyClass::show() 或 obj.show()

常见用途
管理类级别的共享数据(如对象计数)

示例:统计对象数量

#include <iostream>
using namespace std;


class Object {
private:
    static int count;
public:
    Object() { count++; }
    ~Object() { count--; }
    static int getCount() { return count; }
};

int Object::count = 0;  // 初始化

int main() {
    Object a, b;
    cout << Object::getCount()<<endl; // 输出 2
    {
        Object c;
        cout << Object::getCount()<<endl; // 输出 3
    }
    cout << Object::getCount()<<endl; // 输出 2
    return 0;
}

继承

1.概述

继承是面向对象编程的核心机制,允许一个类(派生类,也称子类)基于另一个类(基类,也称父类)来构建,从而复用和扩展已有功能。

2.特点

1.代码重用:子类继承了父类的属性和方法,减少了代码的重复编写。
2.扩展性:子类可以扩展父类的功能,添加新的属性和方法,或者重写(覆盖)现有的方法。
3.多态性:通过继承和虚函数,C++支持多态,允许在运行时决定调用哪个函数。

3.基本用法

在C++中,继承可以是公有(public)、保护(protected)或私有(private)的,这决定了基类成员在派生类中的访问权限。

:表示继承

class Base {
    // ...
};

class Derived : public Base {   // 继承方式:public/protected/private
    // ...
};

4.权限对继承的影响

继承方式 基类 public 成员 基类 protected 成员 基类 private 成员
public 派生类中为 public 派生类中为 protected 不可访问
protected 派生类中为 protected 派生类中为 protected 不可访问
private 派生类中为 private 派生类中为 private 不可访问
访问权限 类内部 同一个类的对象(类的实例) 派生类(子类) 类外部
public ✔可访问 ✔可访问 ✔可访问 ✔可访问
private ✔可访问 ❌不可访问 ❌不可访问 ❌不可访问
protected ✔可访问 ❌不可访问 ✔可访问 ❌不可访问

派生类构造函数

1.概述

在C++中,派生类可以通过其构造函数的初始化列表来调用基类的构造函数。这是在构造派生类对象时初始化基类部分的标准做法。
当创建派生类的对象时,基类的构造函数总是在派生类的构造函数之前被调用

2.示例

1.基类完全没有写任何构造函数

编译器会自动生成一个隐式的默认构造函数,派生类可以正常调用它,不需要在初始化列表中显式写出。

#include <iostream>
using namespace std;

class Base {
    // 完全没有构造函数的声明
    int x;
    // 编译器会生成 Base()
};

class Derived : public Base {
public:
    Derived() {
        cout << "Derived constructed" << endl;
    }
};

int main() {
    Derived d;  // 先调用编译器生成的 Base()
    return 0;
}

基类构造没有打印,但确实被调用了

在这里插入图片描述

2.如果没有明确指定,将调用基类的默认构造函数。
#include <iostream>
using namespace std;

class Base {
public:
    Base() { cout << "Base default constructor" << endl; }
};

class Derived : public Base {
public:
    Derived() { cout << "Derived constructor" << endl; }
    // 初始化列表中没有写 Base(),编译器会自动调用 Base()
};

int main() {
    Derived d;
    return 0;
}

在这里插入图片描述

3.如果基类没有默认构造函数,或者你需要调用一个特定的基类构造函数,就需要在派生类构造函数的初始化列表中明确指定。

基类没有默认的构造函数

#include <iostream>
using namespace std;

class Base {
public:
    Base(int x) : value(x) { cout << "Base(int) called, value=" << value << endl; }
    // 注意:没有定义 Base(),所以没有默认构造函数
private:
    int value;
};

class Derived : public Base {
public:
    // 错误写法:Derived() {}
    // 编译错误:'Base' 没有默认构造函数

    // 正确写法:显式调用 Base(int)
    Derived(int a) : Base(a) { cout << "Derived(int) called" << endl; }
};

int main() {
    Derived d(42);
    return 0;
}

在这里插入图片描述
调用特定的基类构造函数

#include <iostream>
using namespace std;

class Base {
public:
    Base() { cout << "Base default" << endl; }
    Base(string msg) : message(msg) { cout << "Base(string): " << message << endl; }
private:
    string message;
};

class Derived : public Base {
public:
    // 可以选择调用默认构造
    Derived() : Base() { cout << "Derived default" << endl; }

    // 或者调用带参构造
    Derived(string s) : Base(s) { cout << "Derived(string)" << endl; }
};

int main() {
    Derived d1;           // 调用 Base()
    Derived d2("hello");  // 调用 Base(string)
    return 0;
}

在这里插入图片描述

虚函数

1.概述

虚函数是 C++ 实现运行时多态的核心机制,它允许通过基类指针或引用调用派生类中重写的函数版本,在C++中,virtualoverride关键字用于支持多态,尤其是在涉及类继承和方法重写的情况

2.virtual关键字

作用
在基类中声明一个成员函数为虚函数,表示该函数可以被派生类重(override)

特点

  • 只在基类中需要写 virtual;派生类中重写该函数时可以不写(但推荐写 override)。
  • 虚函数可以被继承:一旦基类函数是 virtual,所有派生类的同名同参函数自动成为虚函数。
  • 构造函数不能是 virtual;如果类中有虚函数,析构函数应该是声明为virtual
  • 静态成员函数不能是 virtual(虚函数依赖具体的对象/实例,而静态函数不需要,静态函数属于这个类)

在C++中,成员函数默认不是虚函数。只有显式地使用virtua1关键字才会成为虚函数。

3.override关键字

作用

显式地告诉编译器“这个函数意在重写基类的一个虚函数”。如果实际上并没有成功重写(例如函数名不匹配、基类没有对应的虚函数等),编译器会报错。

4.示例

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show() { cout << "Base::show()" << endl; }
};

class Derived : public Base {
public:
    void show() override { cout << "Derived::show()" << endl; }  // 重写
};
int main() {
    Derived d;
    d.show();
    return 0;
}

虚函数被重写,所以输出不是Base::show()

在这里插入图片描述

多重继承

1.概述

在C++中,多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属性和方法。多重继承增加了语言的灵活性,但同时也引入了额外的复杂性,特别是当多个基类具有相同的成员时。

在多重继承中,派生类继承了所有基类的特性。这包括成员变量和成员函数。如果不同的基类有相同名称的成员,则必须明确指出所引用的是哪个基类的成员

2.示例

#include <iostream>
using namespace std;

class Base {
public:
     void show() { cout << "Base::show()" << endl; }
};

class Derived  {
public:
    void show() { cout << "Derived::show()" << endl; }
};

class c:Base,Derived //同时继承 Base和Derived
{
    public:
        void display()
        {
            Derived::show(); //Base和 Derived都有show,所以需指明
        }
};
int main() {
    Derived d;
    d.show();
    return 0;
}

在这里插入图片描述

菱形继承和虚继承

1.菱形继承

1.概述

两个派生类继承自同一个基类,而另一个类又同时继承这两个派生类,形成的继承结构形如菱形

示例图

      Grand
     /     \
  Base1    Base2
     \     /
     Derived
2.问题

在菱形继承结构中,一个类继承自两个具有共同基类的类时,会导致共同基类的成员在派生类中存在两份拷贝 (Derived继承于Base1和Base2,Base1和Base2又继承于Grand,Grand会被拷贝两份) ,这不仅会导致资源浪费,还可能引起数据不一致的问题。

导致

  • 内存冗余
  • 访问二义性(不知道该用哪一份)

2.虚继承

1.概述

虚继承是C++中一种特殊的继承方式,主要用来解决多重继承中的菱形继承问题。虚继承通过确保共同基类的单一实例存在于继承层次中,来解决菱形继承存在的问题,在继承时使用 virtual 关键字,告诉编译器:该基类在派生类中只保留一份共享实例

3.示例

#include <iostream>
using namespace std;

class Grand{
public:
    int data;
    Grand(int data)
        {
            this->data=data;
        }
    void print()
        {
            cout<<data<<endl;
        }
};
class Base1: virtual public Grand { //虚继承  继承Grand
public:
      Base1(int data):Grand(data) {
     }
};

class Base2:virtual public Grand  { //虚继承 继承Grand
public:
     Base2(int data):Grand(data) {

    }
};

class c:public Base1,public Base2 //同时继承 Base1和Base2
{
    public:
        c(int data):Grand(data),Base1(data),Base2(data) //构造继承要写全,同时要注意顺序
        {

        }
};
int main()
{
   c maomao(8);
   maomao.print();
    return 0;
}

在这里插入图片描述

多态

1.概述

多态是面向对象编程的核心概念之一,指同一接口或符号,在不同类型对象上表现出不同行为。

一个生活类比
假设你有一个遥控器(统一的操作接口),上面有一个“启动”按钮。
你拿这个遥控器去控制空调:按“启动” → 空调开始制冷。
你拿同一个遥控器去控制电视:按“启动” → 电视屏幕亮起。

同样都是按“启动”按钮,结果却不一样。这就是多态:同一个指令,在不同设备上产生不同的效果

2.优点

多态让代码更简短、灵活、易扩展
1. 代码复用,消除重复
不需要为每个派生类单独编写相同的调用逻辑。
例如:一个 render(Shape&) 函数就能处理所有 Circle、Rectangle、Triangle,不用为每种形状重写一遍渲染函数。

2. 高可扩展性
新增派生类(如 Ellipse)时,原有代码无需修改,只要新类正确重写虚函数即可。这降低了引入新功能的成本和风险。

3. 统一接口,降低耦合
调用方只依赖基类的抽象接口,而不依赖具体类型。
例如:render(Shape& s) 不关心 s 到底是什么派生类,,它统一处理所有 Shape 家族

4. 支持面向接口编程
通过纯虚函数定义接口(抽象类),实现“做什么”与“怎么做”分离。
便于团队协作、模块替换、单元测试

3.如何实现多态

通过继承 + 虚函数 + 基类指针/引用实现

1.使用虚函数(Virtual Function):

使用关键字virtual,在基类中定义一个虚函数,这个函数可以在任何派生类中被“重写”

2.创建派生类并重写虚函数:

在派生类中,我们提供该虚函数的具体实现。

3.通过基类的引用或指针调用虚函数:

当我们使用基类类型的指针或引用来调用虚函数时,实际调用的是对象的实际类型(派生类)中的函数版本。

4.示例


#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw() { cout << "绘制图形\n"; }
};

class Circle : public Shape {
public:
    void draw() override { cout << "绘制圆形\n"; }
};

class Rectangle : public Shape {
public:
    void draw() override { cout << "绘制矩形\n"; }
};

// 多态函数:通过基类引用调用
void render(Shape& s) {
    s.draw();   // 运行时决定调用哪个派生类的 draw()
}

int main() {
    Circle c;
    Shape* r=new Rectangle;   //通过基类指针调用
    render(c);   // 输出:绘制圆形
    r->draw();   // 输出:绘制矩形
    return 0;
}

在这里插入图片描述

抽象类

1.概述

抽象类是至少包含一个纯虚函数的类。它不能实例化对象,只能作为基类被继承,用于定义统一的接口规范。

纯虚函数

virtual void func() = 0;   // 纯虚函数,没有函数体

2.特点

  • 不能创建对象(不能实例化)
  • 可以定义指针或引用(用于多态)
  • 派生类必须实现所有纯虚函数,否则派生类也是抽象类
  • 可以包含普通成员函数、成员变量、构造函数(供派生类调用)

3.示例


#include <iostream>
using namespace std;

class Shape {
public:
    virtual void draw()=0;  //纯虚函数
    virtual void comdraw()=0;

};

class Circle : public Shape {
public:  //必须实现所有纯虚函数,否则是抽象类
    void draw() override { cout << "绘制圆形中\n"; }
    void comdraw() override { cout << "绘制圆形完成\n"; }
};

class Rectangle : public Shape {
public:   //必须实现所有纯虚函数,否则是抽象类
    void draw() override { cout << "绘制矩形中\n"; }
    void comdraw() override { cout << "绘制矩形完成\n"; }
};

// 多态函数:通过基类引用调用
void render(Shape& s) {
    s.draw();   // 运行时决定调用哪个派生类的 draw()
    s.comdraw();
}

int main() {
    Circle c;
    render(c);
    Shape* r=new Rectangle;   //通过基类指针调用
    r->draw();
    r->comdraw();
    return 0;
}

在这里插入图片描述

纯虚函数—接口

1.概述

接口是一个编程概念,指一组纯虚函数的集合,用于定义“能做什么”,而不提供具体实现。

C++ 中实现接口

  • 1.定义抽象类
    创建一个包含纯虚函数的抽象类,这些函数构成了接口的一部分。这些函数在抽象类中只有声明而没有具体的实现。

  • 2.派生类实现接口
    派生类继承抽象类,并实现其中的纯虚函数,以具体实现接口定义的方法

一般不用于多态,但是可以多态

2.示例

#include <iostream>
using namespace std;

class eat {
public:
    virtual void eatfood()=0;  //纯虚函数
};
class paiting{
public:
    virtual void tool()=0;  //纯虚函数
};
class person :paiting,eat{
public:
    void eatfood() override{};
    void tool() override{};
};
class dog :eat{
public:
    void eatfood() override{};
};
int main() {
    dog huang;
    person maomao;
    return 0;
}

更多推荐