C++:函数修饰关键字及应用场景

​ virtual

场景:在使用多态特性时,将基类中要被子类重写的函数声明为虚函数/纯虚函数。

tips:

对于多态基类,必须声明为virtual否则释放指向派生类对象的基类指针时可能会内存泄漏

示例:动物基类抽象huntFood方法,子类狗重写对应方法,则需要通过virtual在基类声明。

class Animal
{
private:
    /* data */
public:
    Animal(/* args */);
    ~Animal();

    virtual void huntFood() = 0;
};

class Dog : public Animal{
    public:
        void huntFood() override;

        int getAge() const;
    private:

        int age_;
        float weight_;
};

const

场景:修饰类的成员函数,声明该成员函数不会修改类的成员变量

tips:

1.如果成员变量需要在const函数中修改,可加mutable修饰

2.如果函数返回指针或引用,常用const保证一致性

示例:getAge的具体实现只涉及到读成员变量,不涉及修改,所以用const修饰。


class Dog : public Animal{
    public:
        void huntFood() override;

        int getAge() const;
    private:

        int age_;
        float weight_;
};

override

场景:显示声明重写基类的虚函数,防止隐式转换或者参数不匹配的情况,要求子类严格重写

tips:

建议所有重写虚函数都在后面加上

示例:使用override关键字后,编译器会检测子类的重载是否重载以及是否格式正确,例如参数以及返回值需一致等

class Animal
{
private:
    /* data */
public:
    Animal(/* args */);
    ~Animal();

    virtual void huntFood() = 0;
};

class Dog : public Animal{
    public:
        void huntFood() override;
    private:
};

final

场景:

1.禁止被声明的类被继承

2.禁止虚函数被进一步重写

示例:

1.在Shape后面修饰,代表该类无法被继承

class Shape final {
public:
    virtual void draw() = 0;
};


class Circle : public Shape { }; // ❌ 编译错误

2.修饰在虚函数后,代表更小的子类无法在重写当前虚函数。

class Base {
public:
    virtual void foo();
    virtual void bar();
};

class Derived : public Base {
public:
    void foo() final;   // 允许重写,但禁止 further override
    void bar() override;
};

class Further : public Derived {
public:
    void foo(); // ❌ 错误!foo 在 Derived 中被标记为 final
    void bar(); // ✅ 合法
};

noexcept

场景:声明函数内部不会抛异常,用于类的移动构造和移动赋值构造函数,保证使用vector操作改对象时涉及到扩容支持移动语义扩容,以及部分确定不会抛异常的普通函数中,加快运行速度。

tips:

析构一定noexcept

示例:如果使用std::vector<FileHandle>涉及到扩容时,有下面的noexcept支持就会采取move而非copy

FileHandle(FileHandle &&other) noexcept{
            original_file_ = other.original_file_;
            other.original_file_ = nullptr;
}

default

场景:对类的构造和析构函数使用,声明使用默认的构造和析构,在存在有参构造时不会有默认的无参构造,这里使用default可保证无参构造仍然存在

示例:在存在有参构造的前提下保证类的无参构造能力

class FileHandle {
public:
    // 自定义构造函数
    FileHandle(const char* filename) {
        // 打开文件...
    }

    // 显式要求编译器生成默认构造函数
    FileHandle() = default;

    // 析构函数(可能需要自定义)
    ~FileHandle() {
        if (file_) fclose(file_);
    }

private:
    FILE* file_ = nullptr;
};

delete

场景:使用类的构造,拷贝构造,拷贝赋值构造等构造函数失效,例如单例模式不允许拷贝和拷贝赋值构造,以及单一资源同样不允许。

示例:封装FILE*因为其不能共享,所以通过delete禁止拷贝和拷贝赋值构造

class FileHandle{

    public:
        //构造函数,显示构造函数,防止隐式转换,只能通过FileHandle()来进行显示构造,而不是通过字符串来隐式转换构造(形参指定为FileHandle时就会转换)
        explicit FileHandle(const std::string &path,const std::string &mode);
        //noexcept 析构不能抛异常,默认知道,也可显示声明明确
        ~FileHandle() noexcept;

        //FILE*不可共享,禁止拷贝和赋值
        FileHandle(const FileHandle &) = delete;
        FileHandle& operator=(const FileHandle&) = delete;
        FileHandle(FileHandle &&other) noexcept{
            original_file_ = other.original_file_;
            other.original_file_ = nullptr;
        }

        //赋值构造注意规避自赋值情况,
        FileHandle& operator=(FileHandle&&other) noexcept{
            //赋值的不是自身,就要先关闭自身
            if(this != &other){
                close();
                original_file_ = other.original_file_;
                other.original_file_ = nullptr;
            }

            return *this;
        }
    private:

        FILE* original_file_=nullptr;    

};

explicit

场景:主要用于构造函数和类型转换运算符,要求显式类型转换,防止隐式转换

tips:

对所有单参构造函数都加explicit

示例:

class MyString {
public:
    MyString(int size) { // 允许从 int 隐式构造
        data_ = new char[size];
        size_ = size;
    }

    explicit MyString(int size) { // 禁止隐式转换
        data_ = new char[size];
        size_ = size;
    }
private:
    char* data_;
    int size_;
};

void print(const MyString& s) {
    // 打印字符串...
}

int main() {
    print(10); // ❌ 隐式转换!把 10 当作字符串长度传入
}

嵌入式汇总 文章被收录于专栏

写写记录一下。

全部评论
来一些java和koltin的就好了
点赞 回复 分享
发布于 2025-12-28 23:25 北京

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务