C++最佳实践之常用关键字
C++的常用关键字包括:auto、decltype、try、catch、class、constexpr、new、delete、const_cast、static_cast、dynamic_cast、reinterpret_cast、explicit、export、friend、mutable、using、namespace、noexcept、nullptr、operator、private、pub
C++的常用关键字包括:auto、decltype、bool、throw、try、catch、class、constexpr、new、delete、const_cast、static_cast、dynamic_cast、reinterpret_cast、explicit、export、friend、mutable、using、namespace、noexcept、nullptr、operator、private、protected、public、static_assert、template、typename、this、thread_local、typeid、virtual等。我们结合代码示例来介绍各个关键字。
目录
1、auto自动推断变量
auto根据变量初始化来推断变量类型。示例如下:
auto array = new int[8];
2、decltype推断变量类型
与auto不同的是,decltype是根据表达式来推断变量类型。语法格式如下:
decltype(expression) var;
表达式包括:变量、运算、函数等,示例如下:
long add() {
return 0;
}
void hello() {
int a = 2;
decltype(a) b; // b为int类型
decltype(add()) c; // c为long类型
}
3、bool布尔变量
bool关键字表示布尔变量,只有true或false两种变量值。
4、throw/try/catch异常处理
使用关键字throw抛出异常,使用try/catch来捕获异常。示例代码如下:
try {
std::string msg("This is a test exception");
throw msg;
} catch (const std::string &e) {
printf("exception=%s", e.c_str());
}
5、class类与对象
C++的类与java类相似,都是面向对象编程。类的声明示例如下:
#include <string>
class Person {
private:
int m_age;
std::string m_name;
public:
void setAge(int age);
int getAge();
void setName(const std::string &name);
std::string getName();
};
对应的类实现如下:
#include "Person.h"
void Person::setAge(int age) {
m_age = age;
}
int Person::getAge() {
return m_age;
}
void Person::setName(const std::string &name) {
m_name = name;
}
std::string Person::getName() {
return m_name;
}
创建Person类的对象实例:
auto person = new Person();
person->setAge(10);
person->setName("frank");
printf("name=%s, age=%d", person->getName().c_str(), person->getAge());
6、constexpr常量表达式
constexpr只能修饰带有return的函数。在C++20增加了consteval修饰常量表达式,不同的是,在编译期确定参数类型。示例如下:
constexpr int hello(int a, int b) {
return a + b;
}
另外,函数体内不能有赋值运算,否则有如下报错:
subexpression not valid in a constant expression
7、new与delete管理对象
在C++提供关键字new来创建对象,delete释放对象。在C语言是用库函数malloc来申请内存,free来释放内存。要注意的是,释放数组需要加上[]。示例如下:
// 创建对象
auto person = new Person();
// 释放对象
delete person;
// 创建数组
auto array = new int[8];
// 释放数组
delete[] array;
8、类型转换
C++提供const_cast、static_cast、dynamic_cast和reinterpret_cast四种类型转换,如下表所示:
const_cast | 用于修改const属性,接受指针或引用类型 |
static_cast | 用于基本类型转换 |
dynamic_cast | 用于有继承关系的类指针转换,支持类型检查 |
reinterpret_cast | 用于指针类型转换 |
类型转换的示例代码如下:
// const_cast用于const属性
const int a = 10;
int &b = const_cast<int &> (a);
// static_cast基本类型转换
float x = 10.5f;
int y = static_cast<int> (x);
// dynamic_cast用于子类与父类转换
SuperMan *superman = new SuperMan();
superman->setAge(100);
Person *person = dynamic_cast<Person*> (superman);
// reinterpret_cast用于指针类型转换
void *data = (void *) "hello";
char *new_data = reinterpret_cast<char *> (data);
9、explicit显式调用
explicit用于修饰单参数的构造函数,被修饰的构造函数只能被显式调用,不能被隐式调用。示例如下:
class Person {
private:
int m_age;
public:
explicit Person(int age);
};
10、export全局引用
C语言有extern关键字用于声明全局变量,但是C++的模板没法用extern修饰。因此,提供export修饰在头文件声明的模板类或模板函数,其他源文件只要引用该头文件即可使用模板类或模板函数。
11、friend友元函数
friend关键字把函数声明为友元函数。声明函数为外部类的友元函数后,外部类可以通过友元函数访问该类的私有成员。示例代码如下:
class Pointer;
class Calculator {
public:
Pointer* add(Pointer &a, Pointer &b);
};
class Pointer {
// 声明为Calculator的友元函数
friend Pointer* Calculator::add(Pointer &a, Pointer &b);
private:
int m_x;
int m_y;
public:
Pointer(int x, int y);
};
// Calculator访问Pointer的私有成员,通过对象访问
Pointer* Calculator::add(Pointer &a, Pointer &b) {
return new Pointer(a.m_x + b.m_x, a.m_y + b.m_y);
}
12、mutable可变变量
mutable用于且只能修饰类的成员变量,与const修饰常量相反。比如,constexpr修饰的常量表达式不允许修改成员变量,而成员变量添加mutable修饰符后可修改。示例如下:
class Person {
private:
mutable int m_age;
public:
explicit Person(int age);
constexpr int getAge();
};
constexpr int Person::getAge() {
m_age += 10; // 修改成员变量
return m_age;
}
非成员变量使用mutable修饰符会报错如下:
'mutable' can only be applied to member variable
13、namespace命名空间
命名空间用于模块隔离,避免模块之间命名冲突。示例代码如下:
namespace Learning {
class Person {
private:
std::string m_name;
public:
void setName(const std::string &name);
std::string getName();
};
}
使用using引用命名空间,需要注意的是遵循最小原则。示例如下:
// 直接命名空间引用
Learning::Person *person1 = new Learning::Person();
// 引入命名空间
using namespace Learning;
Person *person2 = new Person();
14、noexcept禁止异常
使用noexcept修饰函数禁止抛出异常,防止错误扩散。示例如下:
std::string getName() noexcept;
15、nullptr空指针
在C语言使用NULL表示空指针,java使用null表示空指针,Object-C使用nil表示空指针。当然,今天的主角是C++,它使用nullptr表示空指针。
16、private、protected和public
与java类似,C++提供private、protected和public访问修饰符,可以修饰类、函数、变量。三者对比如下:
private | 类内部访问 |
proteted | 有继承关系的类可访问 |
public | 类外部都可以访问 |
17、typeid获取类型信息
typeid用于获取类型信息的操作符,使用示例如下:
typeid(a).name()
基本类型对应的类型信息如下表:
基本类型 | 类型信息 |
int | i |
char | c |
short | s |
double | d |
float | f |
long | l |
18、operator重载操作符
在C++中,字符串能够进行加法运算或比较运算,是因为使用operator重载。我们来看下重载加法运算的示例:
class Point {
private:
int m_x;
int m_y;
public:
Point(int x, int y);
int getX();
int getY();
// 重载加法运算
Point operator +(const Point &p) const {
return {m_x + p.m_x, m_y + p.m_y};
}
};
然后调用Point类的加法:
Point p1(1, 2);
Point p2(2, 3);
Point point = p1 + p2;
printf("point.x=%d, point.y=%d\n", point.getX(), point.getY());
19、template模板
template可以用于模板类或模板方法,支持不同数据类型的方法复用。示例如下:
template <typename T>
T add(T a, T b) {
return a + b;
}
然后分别是int类型与float类型的加法运算:
int a = 2, b = 3;
int result1 = add(a, b);
printf("int add=%d\n", result1);
float c = 2.5f, d = 3.5f;
float result2 = add(c, d);
printf("float add=%f\n", result2);
20、this指针
this指针在类内部使用,可以访问类的所有成员。示例如下:
Point::Point(int x, int y) {
this->m_x = x;
this->m_y = y;
}
21、thread_local线程私有
thread_local用于表示线程私有变量,即每个线程都会存储一个变量的值,线程之间互不共享,是C++提供的存储期关键字。与thread_local类似的存储期关键字还有:auto、register、static、extern,各个关键字对比如下:
auto | 自动存储期 |
register | 自动存储期,位于寄存器 |
static | 静态存储期 |
extern | 程序存储期,全局使用 |
thread_local | 线程存储期,线程私有 |
22、virtual虚函数
虚函数是C++的多态机制,使用virtual关键字声明,允许通过基类指针访问基类与派生类的同名函数。基类的析构函数需要声明为虚函数,否则调用不到派生类的析构函数,导致内存泄漏。示例代码如下:
class Animal {
protected:
std::string m_name;
public:
Animal(const std::string &name);
// 析构函数声明为虚函数
virtual ~Animal();
};
class Cat : public Animal {
public:
Cat(const std::string &name);
~Cat();
};
基类与派生类的实现:
Animal::Animal(const std::string &name) {
m_name = name;
}
Animal::~Animal() noexcept {
printf("Animal release\n");
}
Cat::Cat(const std::string &name) : Animal(name) {
m_name = name;
}
Cat::~Cat() noexcept {
printf("Cat release\n");
}
测试代码,创建Cat类,然后释放:
Animal *cat = new Cat("cat");
delete cat;
打印输出如下,先调用派生类析构函数,再调用基类析构函数:
Cat release
Animal release
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)