跳转至

[C++ Basic] Types

导言

  • 除开int、char、float、double、bool、void基本类型,还有枚举、结构体、auto、lambda

User-Defined Types, UDTs

枚举类型 enum

class MemComponent
{
public:
    enum component_t
    {
        INVALID_MEM_COMPONENT = 0,
        MIN_MEM_COMPONENT,
        CORE = MIN_MEM_COMPONENT,
        FIRST_LEVEL_CACHE,
        L1_ICACHE = FIRST_LEVEL_CACHE,
        L1_DCACHE,
        L2_CACHE,
        L3_CACHE,
        L4_CACHE,
        /* more, unnamed stuff follows.
            make sure that MAX_MEM_COMPONENT < 32 as pr_l2_cache_block_info.h contains a 32-bit bitfield of these things
        */
        LAST_LEVEL_CACHE = 20,
        TAG_DIR,
        NUCA_CACHE,
        DRAM_CACHE,
        DRAM,
        MAX_MEM_COMPONENT = DRAM,
        NUM_MEM_COMPONENTS = MAX_MEM_COMPONENT - MIN_MEM_COMPONENT + 1
    };
};
// Usage
MemComponent::component_t A = MemComponent::LAST_LEVEL_CACHE;

结构体

常见结构体使用
struct GrassTp {
int x, y;
int dist;

    //重载运算符  a < other
bool operator < (const GrassTp& other) const {
if (dist == other.dist) {
if (x == other.x)
    return y < other.y;
else
    return x < other.x;
}
return dist < other.dist;
}
};

struct status{
int x, y;
int direct;
    //初始化
status(int x, int y) : x(x), y(y) { direct=0;}
    //重载运算符, 结构体能直接赋值(但是结构体内有指针的时候,可能指针指向空间在其他结构体free的时候释放了)
    // a == b
bool operator==(const status b) const  
    {  
        return (this->x == b.x) && (this->y == b.y) && 
    (this->direct == b.direct);  
    }  
    //允许在结构体当中定义函数, struct中定义的函数和变量都是默认为public的
void forward(){
x += direction[direct][0];
y += direction[direct][1];
}
};


//初始化1
status cur(0,0);
status* cur = new status(0,0);

//初始化2
struct SegmentTp {
int x1, y1;
int x2, y2;
};
SegmentTp segment1 = (SegmentTp){midx - day, midy - day, midx - day, midy + day};

自定义结构体cout

ostream& operator<<(ostream& os, const pair<int, int>& p)
{
    return os << p.first << "\t" << p.second << endl;
}

1. 基本类型 (Built-in Types)

这些类型由语言本身提供,不需要用户定义。包括:

  • 整型 (Integer types):如 intshortlongunsigned int 等。
  • 浮点型 (Floating-point types):如 floatdouble
  • 字符型 (Character types):如 char
  • 布尔型 (Boolean types)bool,值为 truefalse
  • 空类型 (Void type)void,表示无类型,用于函数不返回值时。

2. 复合类型 (Compound Types)

复合类型可以通过将多个基本类型或其他类型组合来创建。包括:

  • 数组 (Array):固定大小的元素序列,所有元素的类型相同。例如:int arr[10];
  • 指针 (Pointer):存储内存地址的类型,表示对另一种类型的间接访问。例如:int* ptr;
  • 引用 (Reference):另一种对变量的别名。例如:int& ref = x;
  • 函数类型 (Function type):函数的声明或定义,也可以将函数作为返回值或参数传递。例如:int foo(int a);
  • 联合体 (Union):类似 struct,但在任何时刻只能存储一个成员。例如:union Data { int i; float f; };

四种智能指针

C++中常用的四种智能指针及其区别简介如下:

  1. unique_ptr

  2. 独占式拥有权的智能指针,adopted对象只能被一个unique_ptr所拥有

  3. 不能复制,只能移动(move semantics)
  4. 删除时会自动释放对象
  5. 不能释放获取(get)到的原始指针

  6. shared_ptr

  7. 共享式拥有权的智能指针,多个shared_ptr可以共享同一个对象

  8. 使用引用计数进行自动内存管理
  9. 获取原始指针不会导致引用计数更改
  10. 线程安全,可在多线程环境下使用

  11. weak_ptr

  12. 弱引用智能指针,指向shared_ptr管理的对象

  13. 不会影响对象的生命周期,不增加引用计数
  14. 可以判断共享对象是否已被释放

  15. auto_ptr (C++03后废弃)

  16. 采用所有权转移的方式

  17. 失去作用域后会自动释放对象
  18. 赋值或者复制会导致所有权的转移,无法实现共享

上述智能指针各有特点,使用场景不同,合理利用可以简化资源管理,提高代码安全性。

unique_ptr 不可以复制

指它不支持复制构造函数和复制赋值操作符。

这ensure了一个对象只能被一个unique_ptr独占式拥有,避免出现两个unique_ptr同时管理同一个对象导致双重释放等问题。

例如下面的代码会报错:

std::unique_ptr<int> p1(new int(1));
std::unique_ptr<int> p2 = p1; // 错误,unique_ptr不可以复制

但是,unique_ptr支持移动(move)语义。移动语义是C++11新增的特性,它可以避免复制的开销。

例如:

std::unique_ptr<int> p1(new int(1));
std::unique_ptr<int> p2 = std::move(p1); // ok, p1移动到p2
// p1现在为空

move函数会将p1的内容“转移”给p2,此后p1为空,p2获得了对象的独占式拥有权。

移动语义可以将对象的资源“传递”给另一个指针,避免拷贝,提高性能。

这个特性可以利用std::move函数实现。所以unique_ptr可以移动但不可以复制。

使用shared_ptr来管理对象的生命周期

可以这样修改:

#include <memory>

class TrieWithPos {
public:
TrieWithPos(TreeNode* root, int pos) : pos(pos) {
    if (root->left) {
    left = std::make_shared<TrieWithPos>(root->left, 2 * pos); 
    }
    if (root->right) {
    right = std::make_shared<TrieWithPos>(root->right, 2 * pos + 1);
    }
}

private:
int pos;
std::shared_ptr<TrieWithPos> left;
std::shared_ptr<TrieWithPos> right;
};

主要改动:

  • left和right成员改为shared_ptr类型
  • 使用make_shared来创建对象,自动管理生命周期
  • 析构函数不再需要手动delete
  • shared_ptr能自动释放对象,不需担心内存泄漏

这样就可以通过shared_ptr来简化生命周期的管理,使代码更加安全。

二维指针与二维数组

声明

int **p;

p = (int**)malloc(sizeof(int*)*m); //开辟行

for(i = 0; i < m; i++)
{
    *(p+i) = (int*)malloc(sizeof(int)*n);//开辟列
}

相互复制

#define N 2048
int a[N][N] = {0};
int (*A)[2048];
A=a;
A[i][j] // 如果A声明没有2048,就int **A那么A[i][j]不知道每行多少个元素

二维vector传参

vector<vector<double>> copy_off_tree_edge;

adjust_similarity_tree(i, &bfs_process1, &bfs_process2, similarity_tree, &copy_off_tree_edge);

void adjust_similarity_tree(int i, std::vector<int> *bfs_process1, std::vector<int> *bfs_process2 ,\
                            int *similarity_tree, vector<vector<double>> *copy_off_tree_edge){
                                ...
                                (* copy_off_tree_edge)[z][0]

3. 类型别名 (Type Aliases)

  • typedefusing:用于为现有的类型定义别名。例如:
  • typedef unsigned long ulong;
    using ulong = unsigned long;
    

4. 模板类型 (Template Types)

  • 类模板 (Class Template):允许定义泛型类,可以处理不同的数据类型。例如:std::vector<T>
  • 函数模板 (Function Template):定义可用于多个类型的函数。例如:

    template <typename T>
    T add(T a, T b) {
        return a + b;
    }
    

5. decltypeauto 类型

  • decltype:用于推导表达式的类型。例如:decltype(x) 将推导出 x 的类型。
  • auto:让编译器自动推断变量的类型。例如:auto x = 10; 会自动推断 xint

decltype

// 不是lambdas的,定义函数写法
auto hash = [](const std::pair<int, int>& p){ return p.first * 31 + p.second; };
std::unordered_set<std::pair<int, int>, decltype(hash)> u_edge_(8, hash);

decltype 生成指定表达式的类型,有点类似auto。

6. std::tuplestd::pair

  • 元组 (Tuple):存储多个不同类型的数据。例如:std::tuple<int, std::string, float> tup;
  • 对组 (Pair):存储两个可能是不同类型的值。例如:std::pair<int, std::string> p;

7. std::variantstd::any

  • std::variant:可以存储多个类型中的一个值,但在任何时刻只能存储一个。例如:std::variant<int, float> v;
  • std::any:可以存储任意类型的值,允许更动态地处理类型。例如:std::any a = 42;

8. Lambda 表达式类型

  • Lambda 表达式:一种匿名函数,可以捕获所在作用域中的变量并执行。例如:auto f = [](int x) { return x + 1; };

匿名函数由以下几个部分组成,其中只有 1, 2, 6 三个部分是必须的,其余部分可以省略:

  1. 捕获子句 capture clause / lambda introducer
  2. 捕获子句用于捕获外部变量,使得匿名函数体可以使用这些变量,捕获的方法分为引用捕获值(拷贝)捕获两种,使用方法如下:
    1. [] 不捕获任何变量;
    2. 使用默认捕获模式来指示如何捕获 Lambda 体中引用的任何外部变量, 使用默认捕获时,只有 Lambda 体中提及的变量才会被捕获。
      1. [&] 按引用捕获所有外部变量;
        1. 不建议使用 2,3 这两种方式进行捕获(对性能影响较大),应该明确地指出需要按引用捕获的变量;
        2. 按引用捕获的变量(或按值捕获的指针),如果该引用变量(或指针指向的对象)在外部被析构,那么匿名函数中的引用变量(或指针)则会成为悬空引用/指针(Dangling Pointer)
      2. [=] 按值捕获所有外部变量
        1. 按值捕获的变量是 read-only (const) 的,只有当匿名函数的可变规格被显式声明为 mutable 的时候才可以修改按值捕获的变量;
        2. 按值捕获的变量的值在匿名函数生成的时候就已经确定了,如果在匿名函数生成后修改外部变量的值,则不会影响到匿名函数内被捕获的变量值
    3. [&, var] 默认按引用捕获,仅按值捕获 var;
    4. [=, &var] 默认按值捕获,仅按引用捕获 var;
    5. 由于使用默认捕获时,只有 Lambda 体中提及的变量才会被捕获。下列四种表达等价[&total, factor] [factor, &total] [&, factor] [=, &total]
  3. 参数列表 parameter list / lambda declarator
  4. 可变规格 mutable specification,被 mutable 修饰的匿名函数可以修改按值捕获的变量
  5. 异常设定 exception specification
  6. 尾随返回类型 trailing-return-type
  7. 匿名函数体 lambda body
可以公用grid等数组,而且相当于private
#include<functional> 
int getMaximumGold(vector<vector<int>>& grid) {
    // 将lambda表达式赋值给 函数变量 dfs
    function<void(int, int, int)> dfs = [&](int x, int y, int gold) {
        gold += grid[x][y];
        ……
        grid[x][y] = rec;
    };
    dfs(10, 30, 0);
}

//C++11 using lambdas
sort(arr.begin(), arr.end(), 
[](const pair<int, char> & p1, const pair<int, char> & p2) {
return p1.first > p2.first;
}
);

9. enum class (Scoped Enum)

  • 与传统的 enum 不同,enum class 是作用域枚举,不能隐式转换为整数,提供更强的类型安全。例如:
    enum class Color { Red, Green, Blue };
    

参考文献

评论