当前位置: 首页 > article >正文

【编程贴士】编写类与函数时,什么时候用const、static、inline等关键字?(C、C++)

文章目录

  • 前言
  • 相关概念
    • 什么时候使用?const
    • 什么时候使用?inline
    • 什么时候使用?explicit
    • 什么时候使用?friend
    • 什么时候使用?static
    • 什么时候使用?virtual


前言

我们在编写一个程序时,往往需要编写不同的类,函数功能等,根据不同的应用场景会需要不同的关键字、才能保证写出的代码安全,有效。下面我们就来介绍如何正确使用这些关键字
(ps:自己写代码时发现部分关键字功能用不太好,想了想干脆总结一下)


相关概念

在使用时最好还是理解这些关键字的相关概念,下面会一一列出。在后面应用时也会解释:

const 关键字

深入解析 const 关键字:指针、参数、返回值和类成员函数

inline 关键字

【C++】探索inline关键字:用法、限制与示例代码

static 关键字

【C++】static 关键字的特性 与 问题

friend 关键字

【C++】解析friend关键字:概念、友元函数与类的使用

virtual 关键字(继承)

【c++】继承详解(菱形 / 虚拟继承)


什么时候使用?const

  1. const函数
    • 当一个成员函数不修改类的成员变量时,应该将其声明为const。这表明该函数不会改变对象的状态,有助于在编译时进行优化和保证代码的正确性。例如:
     class MyClass {
     public:
         void print() const { // 不修改任何成员变量
     	 	std::cout << "hello world" << std::endl;
     	 }
     };
  1. const参数
    • 当参数传递给函数时,如果参数在函数内部不需要被修改,应将其声明为const,尤其是对于引用或指针类型。这有助于明确函数不会改变传入的数据,提高代码的安全性和可读性。例如:
   void process(const int& data); // data 在函数内不被修改

对于大多输入型参数一般使用 const type& x; 的形式


  1. 不需要const的参数
    • 如果函数需要修改传入的参数,或如果参数是基本数据类型(如int),通常不需要const。例如:
   void update(int& data); // data 在函数内可能被修改

对于大多输出型参数一般使用 type* x 或 type& x


什么时候使用?inline

inline 关键字 在 C++ 和 C 编程语言中用于指示编译器尝试将函数的代码直接插入到调用它的地方,而不是在程序中生成一个函数调用。这种方式可以减少函数调用的开销,可能提高程序的执行效率(取决于编译器的优化策略)

所在在类内一些偏简单的函数,可以使用inline:

class MyClass {
public:
    inline void setValue(int v) { value = v; }
    inline int getValue() const { return value; }
private:
    int value;
};

什么时候使用?explicit

explicit 关键字在 C++ 中用于防止隐式类型转换,确保构造函数和转换运算符只在显式调用时执行。当你定义一个构造函数或转换运算符时,如果希望防止该函数在赋值或初始化时被自动调用以进行隐式转换,可以使用 explicit。例如:

class MyClass {
public:
    explicit MyClass(int value); // 防止隐式转换
};

// 使用
MyClass obj1 = 5; // 错误:不能进行隐式转换,因为构造函数是 explicit
MyClass obj2(5);  // 正确:显式调用构造函数

在这个例子中,explicit 确保只有在显式地调用 MyClass(int) 构造函数时才会进行转换,而不是在赋值或初始化时自动转换。


什么时候使用?friend

friend通常用于友元函数、友元类、友元成员函数的声明,其允许非成员函数或其他类访问类的私有成员和保护成员:

  1. 友元函数

    • 友元函数是一个非成员函数,但它可以访问类的私有成员和保护成员。声明友元函数的语法如下:
    class MyClass {
    private:
        int privateData;
    public:
        MyClass() : privateData(0) {}
    
        // 声明友元函数
        friend void showData(const MyClass& obj);
    };
    
    // 定义友元函数
    void showData(const MyClass& obj) {
        std::cout << "Private data: " << obj.privateData << std::endl;
    }
    

    对于本例:showData 函数被声明为 MyClass 的友元函数,因此它可以访问 privateData 成员。


  1. 友元类

    • 友元类可以访问另一个类的私有和保护成员。
    class MyClassB; // 前向声明
    
    class MyClassA {
    private:
        int privateData;
    public:
        MyClassA() : privateData(42) {}
    
        // 声明 MyClassB 为友元类
        friend class MyClassB;
    };
    
    class MyClassB {
    public:
        void display(const MyClassA& obj) {
            std::cout << "Private data from MyClassA: " << obj.privateData << std::endl;
        }
    };
    

    对于本例:MyClassB 被声明为 MyClassA 的友元类,因此 MyClassB 可以访问 MyClassAprivateData 成员。


  1. 友元成员函数

    • 如果你希望其他类的某个特定成员函数可以访问类的私有和保护成员,可以声明这个成员函数为友元:
    class MyClassC;
    
    class MyClassA {
    private:
        int privateData;
    public:
        MyClassA() : privateData(99) {}
    
        // 声明 MyClassC 的成员函数 foo 为友元
        friend void MyClassC::foo(const MyClassA& obj);
    };
    
    class MyClassC {
    public:
        void foo(const MyClassA& obj) {
            std::cout << "Private data from MyClassA: " << obj.privateData << std::endl;
        }
    };
    

    对于本例:MyClassC 的成员函数 foo 被声明为 MyClassA 的友元,因此 foo 可以访问MyClassAprivateData 成员。


什么时候使用?static

  1. 静态局部变量:在函数内部声明为 static 的变量在函数调用之间保持其值。例如:

    void counter() {
        static int count = 0;
        count++;
        std::cout << count << std::endl;
    }
    

    每次调用 counter 函数时,count 的值不会被重置。

  2. 静态成员变量:在类中声明为 static 的成员变量被所有对象共享,属于类而非某个特定对象。例如:

    class MyClass {
    public:
        static int staticVar;
    };
    
    int MyClass::staticVar = 0;
    

    可以通过类名来访问它,如 MyClass::staticVar

  3. 静态成员函数:在类中声明为 static 的成员函数只能访问静态成员变量和其他静态成员函数。例如:

    class MyClass {
    public:
        static void staticFunc() {
            // 只能访问静态成员
        }
    };
    
  4. 静态全局变量和函数:在文件级作用域中声明为 static 的变量或函数只对当前文件可见,避免了名称冲突。例如:

    static int localVar = 0; // 只在当前文件中可见
    
    static void localFunc() {
        // 只在当前文件中可见
    }
    

什么时候使用?virtual

在 C++ 中,virtual 关键字用于实现多态性(Polymorphism)。

  1. 虚拟函数(Virtual Functions)

虚拟函数用于在基类中定义接口,使得派生类可以重写这些接口的方法。这是实现运行时多态的主要方式。使用 virtual 关键字声明虚拟函数时,派生类可以重写(override)这个函数,而实际调用的函数将取决于对象的实际类型(即运行时类型)。示例如下:

#include <iostream>

class Base {
public:
    virtual void show() { // 声明为虚拟函数
        std::cout << "Base class show()" << std::endl;
    }
};

class Derived : public Base {
public:
    void show() override { // 重写虚拟函数
        std::cout << "Derived class show()" << std::endl;
    }
};

int main() {
    Base* b;
    Derived d;

    b = &d;

    b->show(); // 输出: "Derived class show()"
    return 0;
}

在这个例子中,即使 bBase 类型的指针,但它实际指向 Derived 类型的对象。调用 b->show() 时,运行时决定调用 Derived 类的 show() 方法。

  1. 虚拟析构函数(Virtual Destructors)

如果你使用基类指针来删除派生类对象,那么基类的析构函数应该是虚拟的,以确保派生类的析构函数也会被调用,避免资源泄漏。例如:

#include <iostream>

class Base {
public:
    virtual ~Base() { // 虚拟析构函数
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() override { // 重写析构函数
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Base* b = new Derived();
    delete b; // 调用 Derived 的析构函数,然后调用 Base 的析构函数
    return 0;
}

在这个例子中,如果 Base 类的析构函数不是虚拟的,则 delete b 只会调用 Base 类的析构函数,Derived 类的析构函数不会被调用,这可能导致资源泄漏。

  1. 纯虚拟函数(Pure Virtual Functions)

纯虚拟函数是虚拟函数的一种特殊情况,声明为纯虚拟函数的类称为抽象类,不能被实例化。派生类必须重写所有纯虚拟函数才能成为具体类。声明纯虚拟函数的语法如下:

class AbstractBase {
public:
    virtual void pureVirtualFunction() = 0; // 纯虚拟函数
};

在这个例子中,AbstractBase 是一个抽象类,不能直接实例化。任何派生自 AbstractBase 的类必须实现 pureVirtualFunction() 才能实例化。

  1. 虚拟继承(Virtual Inheritance)

虚拟继承用于解决多重继承中的菱形继承问题。它确保只有一个基类子对象实例被共享。虚拟继承声明如下:

class A {
public:
    virtual void foo() {}
};

class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

在这个例子中,BC 都虚拟继承自 A,从而在 D 类中只有一个 A 类的实例。


http://www.kler.cn/a/291523.html

相关文章:

  • 20241116解决在WIN11和ubuntu20.04通过samba共享时出现局域网千兆带宽拉满的情况
  • 小试牛刀-Anchor安装和基础测试
  • MODBUS TCP转CANOpen网关
  • React Native 全栈开发实战班 - 打包发布之热更新
  • 有限状态机(续)
  • C# DataTable使用Linq查询详解
  • 移动端设计规范:提升用户体验的核心要素
  • 基于阿里云函数计算(FC)x 云原生 API 网关构建生产级别 LLM Chat 应用方案最佳实践
  • F - Simplified Reversi 矩阵侧边视角 修改
  • Python Invoke:强大的自动化任务库
  • C++ 重载运算符和重载函数
  • 构建全景式智慧文旅生态:EasyCVR视频汇聚平台与AR/VR技术的深度融合实践
  • Spark MLlib模型训练—回归算法 Linear regression
  • 不限专业和工作经验,这个含金量巨高的IT证书,90%的大学生都不知道!
  • FPGA 编程基础, 赋值操作符, 运算符使用, 条件表达式, 信号操作方法
  • 工业应用软件开发实训室(边缘计算)建设方案
  • sportbugs报告路径在linux和windows中的配置差异
  • Linux 文件操作相关函数整理
  • 基于django的在线音乐网站设计/基于python的音乐播放系统
  • Node.js模块系统
  • C#转java工具
  • 鸿蒙轻内核M核源码分析系列二一 02 文件系统LittleFS
  • 【C++设计模式】(三)创建型模式:单例模式
  • 前端框架对比
  • 学习netty 从哪里开始
  • 【Java毕业设计】基于SpringBoot+Vue+uniapp的农产品商城系统