【编程贴士】编写类与函数时,什么时候用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
const
函数:- 当一个成员函数不修改类的成员变量时,应该将其声明为
const
。这表明该函数不会改变对象的状态,有助于在编译时进行优化和保证代码的正确性。例如:
- 当一个成员函数不修改类的成员变量时,应该将其声明为
class MyClass {
public:
void print() const { // 不修改任何成员变量
std::cout << "hello world" << std::endl;
}
};
const
参数:- 当参数传递给函数时,如果参数在函数内部不需要被修改,应将其声明为
const
,尤其是对于引用或指针类型。这有助于明确函数不会改变传入的数据,提高代码的安全性和可读性。例如:
- 当参数传递给函数时,如果参数在函数内部不需要被修改,应将其声明为
void process(const int& data); // data 在函数内不被修改
对于大多输入型参数一般使用 const type& x; 的形式
- 不需要
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通常用于友元函数、友元类、友元成员函数的声明,其允许非成员函数或其他类访问类的私有成员和保护成员:
-
友元函数
- 友元函数是一个非成员函数,但它可以访问类的私有成员和保护成员。声明友元函数的语法如下:
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
成员。
-
友元类
- 友元类可以访问另一个类的私有和保护成员。
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
可以访问MyClassA
的privateData
成员。
-
友元成员函数
- 如果你希望其他类的某个特定成员函数可以访问类的私有和保护成员,可以声明这个成员函数为友元:
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
可以访问MyClassA
的privateData
成员。
什么时候使用?static
-
静态局部变量:在函数内部声明为
static
的变量在函数调用之间保持其值。例如:void counter() { static int count = 0; count++; std::cout << count << std::endl; }
每次调用
counter
函数时,count
的值不会被重置。 -
静态成员变量:在类中声明为
static
的成员变量被所有对象共享,属于类而非某个特定对象。例如:class MyClass { public: static int staticVar; }; int MyClass::staticVar = 0;
可以通过类名来访问它,如
MyClass::staticVar
。 -
静态成员函数:在类中声明为
static
的成员函数只能访问静态成员变量和其他静态成员函数。例如:class MyClass { public: static void staticFunc() { // 只能访问静态成员 } };
-
静态全局变量和函数:在文件级作用域中声明为
static
的变量或函数只对当前文件可见,避免了名称冲突。例如:static int localVar = 0; // 只在当前文件中可见 static void localFunc() { // 只在当前文件中可见 }
什么时候使用?virtual
在 C++ 中,virtual
关键字用于实现多态性(Polymorphism)。
- 虚拟函数(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;
}
在这个例子中,即使 b
是 Base
类型的指针,但它实际指向 Derived
类型的对象。调用 b->show()
时,运行时决定调用 Derived
类的 show()
方法。
- 虚拟析构函数(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
类的析构函数不会被调用,这可能导致资源泄漏。
- 纯虚拟函数(Pure Virtual Functions)
纯虚拟函数是虚拟函数的一种特殊情况,声明为纯虚拟函数的类称为抽象类,不能被实例化。派生类必须重写所有纯虚拟函数才能成为具体类。声明纯虚拟函数的语法如下:
class AbstractBase {
public:
virtual void pureVirtualFunction() = 0; // 纯虚拟函数
};
在这个例子中,AbstractBase
是一个抽象类,不能直接实例化。任何派生自 AbstractBase
的类必须实现 pureVirtualFunction()
才能实例化。
- 虚拟继承(Virtual Inheritance)
虚拟继承用于解决多重继承中的菱形继承问题。它确保只有一个基类子对象实例被共享。虚拟继承声明如下:
class A {
public:
virtual void foo() {}
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
在这个例子中,B
和 C
都虚拟继承自 A
,从而在 D
类中只有一个 A
类的实例。