常用的关键特性
目录
一、字面量。
二、静态断言。
三、构造函数分类。
1,构造函数。
2,析构函数。
四、default和delete。
五、override和final。
六、constexpr。
作为一种广泛使用的编程语言,C++具有无可争议的强大的功能性和灵活性。从系统编程到游戏开发,从嵌入式到上位机,C++无处不在。随着版本的不断演进,新的特性被引入。以下列举探讨一些开发中常用的关键特性。
一、字面量。
字面量是程序中直接表示固定值的表达式,具有很强的语义清晰度。C++ 支持多种类型的字面量,如整数字面量、浮点字面量、字符字面量、字符串字面量和布尔字面量。使用合适的字面量可以提高代码的可读性和维护性。
编译阶段的处理
在 C++ 的编译阶段,字面量被视为常量表达式,编译器将其直接嵌入生成的机器代码中。这种处理方式使得字面量的使用非常高效,因为它们不需要在运行时动态分配内存。例如,整数字面量 10
在编译时会被直接替换为其值,不会产生额外的内存开销。
编译器在解析字面量时会根据其类型进行适当的处理和类型推导。例如,浮点字面量可能会被处理为 float
或 double
,具体取决于字面量的格式(如 3.14f
表示 float
)。对于字符串字面量,编译器会生成相应的字符数组,并在内部处理其存储和生命周期。
#include <iostream>
int main() {
int a = 10; // 整数字面量
double b = 3.14; // 浮点字面量
char c = 'A'; // 字符字面量
const char* str = "Hello"; // 字符串字面量
std::cout << a << " " << b << " " << c << " " << str << std::endl;
return 0;
}
常用场景
在项目中,字面量常用于配置常量和替代魔法数字。使用命名常量(如 const int MAX_USERS = 100;
)替代字面量,不仅提高了代码的可读性,还减少了出错的几率。此外,确保使用合适的数据类型(如 int
、float
)可以避免由于隐式转换导致的精度问题。
二、静态断言。
静态断言是编译时检查某个条件的工具,特别适用于模板编程。在编写泛型代码时,确保类型的有效性是至关重要的。
在进行底层库和框架开发时,静态断言通常用于确保模板参数的有效性。通过在模板中使用静态断言,可以在编译时提供清晰的错误信息,减少运行时错误的可能性。为了提高代码的可维护性,建议在静态断言中提供清晰且具体的错误信息。
#include <iostream>
#include <type_traits>
template <typename T>
void check() {
static_assert(std::is_integral<T>::value, "T must be an integral type");
}
int main() {
check<int>(); // 编译通过
// check<double>(); // 编译错误
return 0;
}
三、构造函数分类。
C++ 中的成员函数可以根据需要进行多种类型的修饰,包括构造函数、析构函数、拷贝构造函数、移动构造函数等。
1,构造函数。
构造函数是用于初始化类对象的特殊成员函数。C++ 支持多种类型的构造函数:
默认构造函数
无参数的构造函数,用于创建对象时赋予默认值。
class MyClass {
public:
MyClass() {
// 默认构造函数
}
};
参数化构造函数
带参数的构造函数,用于根据给定值初始化对象。
class MyClass {
public:
MyClass(int x) {
// 参数化构造函数
}
};
拷贝构造函数
用于通过另一个同类对象初始化新对象。
class MyClass {
public:
MyClass(const MyClass& other) {
// 拷贝构造函数
}
};
移动构造函数(C++11 及以后)
用于通过右值引用移动资源。
class MyClass {
public:
MyClass(MyClass&& other) noexcept {
// 移动构造函数
}
};
2,析构函数。
析构函数在对象生命周期结束时调用,用于释放资源。每个类只能有一个析构函数。
class MyClass {
public:
~MyClass() {
// 析构函数
}
};
-
默认构造函数:在需要创建对象时,无需提供初始值的情况下使用,适合类有合理的默认状态时。
-
参数化构造函数:在对象创建时根据特定值初始化,适合需要定制化初始状态的情况。
-
拷贝构造函数:在需要深拷贝对象的情况下使用,确保源对象和目标对象各自拥有独立的资源,避免潜在的资源共享问题。
-
移动构造函数:在性能敏感的情况下使用,特别是涉及动态内存分配时,可以避免不必要的深拷贝,提升性能。
#include <iostream>
#include <vector>
class MyClass {
public:
MyClass() {
std::cout << "Default constructor called." << std::endl;
}
MyClass(int x) {
std::cout << "Parameterized constructor called with value: " << x << std::endl;
}
MyClass(const MyClass& other) {
std::cout << "Copy constructor called." << std::endl;
}
MyClass(MyClass&& other) noexcept {
std::cout << "Move constructor called." << std::endl;
}
~MyClass() {
std::cout << "Destructor called." << std::endl;
}
};
int main() {
MyClass obj1; // 调用默认构造函数
MyClass obj2(42); // 调用参数化构造函数
MyClass obj3 = obj2; // 调用拷贝构造函数
MyClass obj4 = std::move(obj2); // 调用移动构造函数
return 0;
}
四、default和delete。
C++11 引入了 default
和 delete
关键字,以便于更精确地控制构造函数、赋值运算符和析构函数的生成或禁止。这些特性在对资源管理的使用场景中尤为重要。
在处理资源管理(如内存、文件句柄等)时,使用 delete
禁止拷贝构造和赋值是最佳实践。这确保了资源的独占性,避免了潜在的双重释放或内存泄漏问题。而 default
可以用于简单类的默认行为生成,保持代码简洁和可读。
class MyClass {
public:
MyClass() = default; // 默认构造函数
MyClass(const MyClass&) = delete; // 禁止拷贝构造
MyClass& operator=(const MyClass&) = delete; // 禁止拷贝赋值
};
int main() {
MyClass obj1; // 可以创建对象
// MyClass obj2 = obj1; // 编译错误
return 0;
}
五、override和final。
override
和 final
关键字在多态编程中应用较多,可用于控制虚函数的重写和继承,帮助我们更清晰地表达意图,通过前期编译检查,避免潜在错误。
在设计类层次结构时,建议使用 override
来标识重写的方法。这不仅有助于代码的可读性,还能在基类方法签名发生变化时,及时捕获错误。此外,使用 final
可以防止不必要的重写,从而增强接口的稳定性。
class Base {
public:
virtual void show() {
std::cout << "Base show" << std::endl;
}
virtual void print() final {
std::cout << "Base print" << std::endl;
}
};
class Derived : public Base {
public:
void show() override {
std::cout << "Derived show" << std::endl;
}
// void print() override { } // 编译错误
};
int main() {
Derived d;
d.show(); // Derived show
d.print(); // Base print
return 0;
}
六、constexpr。
constexpr
关键字使得函数和变量可以在编译时求值,有助于在常量表达式中提升性能。这一特性对于性能敏感的应用场景尤为重要。 在模板编程和常量表达式求值的场景中,constexpr
的使用可以显著提升运行时性能。要注意,constexpr
函数的实现必须符合特定要求,如所有参数必须为常量表达式,并且不能包含运行时决定的控制流。
#include <iostream>
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5);
std::cout << "Factorial of 5 is: " << result << std::endl; // 120
return 0;
}