Skip to content

explicit

explicit 表示:禁止编译器使用这个单参数构造函数进行隐式类型转换。

假设没有 explicit

class Type {
public:    
	Type(TypeId type_id) : type_id_(type_id) {}
};

那么下面这种写法可能成立:

TypeId id = ...;
Type t = id;   // 隐式调用 Type(id)

加上 explicit 后:

explicit Type(TypeId type_id) : type_id_(type_id) {}

只能显式构造:

Type t(id);        // 可以
Type t{id};        // 可以
Type t = Type(id); // 可以
Type t = id;       // 不可以

explicit 的主要用途是避免意外的隐式转换。

enum 使用示例

CmpBool a = GetCmpBool(true);
CmpBool b = GetCmpBool(false);
CmpBool c = CmpBool::kNull;

结果分别是:

a == CmpBool::kTrue
b == CmpBool::kFalse
c == CmpBool::kNull

可以写判断逻辑:

CmpBool result = GetCmpBool(10 > 3);
if (result == CmpBool::kTrue) { 
 // 比较成立
 }

enum class

推荐使用 enum class

enum class CmpBool {  kFalse = 0,  kTrue,  kNull};
inline CmpBool GetCmpBool(bool boolean) {  
	return boolean ? CmpBool::kTrue : CmpBool::kFalse;
}

enum class 的类型约束更严格,不会把枚举项暴露到外层作用域,也不会轻易隐式转换成整数。

例如:

CmpBool value = CmpBool::kTrue;

而下面这种写法不会成立:

int number = CmpBool::kTrue;  // enum class 下编译失败

需要显式转换:

int number = static_cast<int>(CmpBool::kTrue);

在现代 C++ 工程中,enum class 通常比普通 enum 更安全。

Learn macros.h:

c++
#ifndef MINISQL_MACROS_H
#define MINISQL_MACROS_H

#include <cassert>

#define ASSERT(expr, message) assert((expr) && (message))

// Macros to disable copying and moving

#define DISALLOW_COPY(cname)                             \

  cname(const cname &) = delete;            /* NOLINT */ \

  cname &operator=(const cname &) = delete; /* NOLINT */

#define DISALLOW_MOVE(cname)                        \

  cname(cname &&) = delete;            /* NOLINT */ \

  cname &operator=(cname &&) = delete; /* NOLINT */

#define DISALLOW_COPY_AND_MOVE(cname) \

  DISALLOW_COPY(cname);               \

  DISALLOW_MOVE(cname);

#define MACH_WRITE_TO(Type, Buf, Data)       \

  do {                                       \

    *reinterpret_cast<Type *>(Buf) = (Data); \

  } while (0)

#define MACH_WRITE_UINT32(Buf, Data) MACH_WRITE_TO(uint32_t, (Buf), (Data))

#define MACH_WRITE_INT32(Buf, Data) MACH_WRITE_TO(int32_t, (Buf), (Data))

#define MACH_WRITE_STRING(Buf, Str)         \

  do {                                      \

    memcpy(Buf, Str.c_str(), Str.length()); \

  } while (0)

#define MACH_READ_FROM(Type, Buf) (*reinterpret_cast<const Type *>(Buf))

#define MACH_READ_UINT32(Buf) MACH_READ_FROM(uint32_t, (Buf))

#define MACH_READ_INT32(Buf) MACH_READ_FROM(int32_t, (Buf))

#define MACH_STR_SERIALIZED_SIZE(Str) (4 + Str.length())

#define ALLOC(Heap, Type) new (Heap.Allocate(sizeof(Type))) Type

#define ALLOC_P(Heap, Type) new (Heap->Allocate(sizeof(Type))) Type

#define ALLOC_COLUMN(Heap) ALLOC(Heap, Column)

#endif  // MINISQL_MACROS_H

inline

基本上是给函数用的

现代 C++ 编译器会自行决定是否内联优化,所以现在 inline 的更重要的作用是允许函数定义直接写到头文件 .h 里而不会引起 multiple definition fault 。

For instance:

cpp
inline const RowId GetRowId() const {
  return rid_;
}

这里 getter 很短,所以直接写在头文件里了。

this

class 里的成员函数一般先在当前函数内找变量,找不到再到成员变量里找。

一般:

cpp
class A {
private:
    int val = 10;

public:
    int get() {
        return val;        // 等价于 return this->val;
    }
};

class 里存在同名遮蔽,只有在需要消除歧义时需要显示写 this->val

cpp
class A {
private:
    int val = 10;

public:
    void set(int val) {
        val = val;         // 两边都是参数 val,成员变量没有被修改
        this->val = val;   // 左边是成员变量,右边是参数
    }
};

void print() const { cout << val; } 这里的const表示什么?

这里的 const 表示:这个成员函数不会修改当前对象的成员变量

cpp
class Counter{
void print() const { cout << val; }
}

等价含义是:

cpp
void print(const Counter* this)

前置 ++c 和后置 c++ 是怎么区分的?

函数签名区分。

在 C++ 里,前置 ++c 和后置 c++ 都可以重载,但写法不同:

// 前置 ++cCounter& operator++() {    ++val;    return *this;}// 后置 c++Counter operator++(int) {    Counter old = *this;    ++val;    return old;}

关键区别是:

operator++()       // 前置++operator++(int)    // 后置++

后置版本多了一个假的 int 参数。这个参数只是给编译器区分前置/后置用的,通常不用它。


调用关系:

++c;

编译器翻译成:

c.operator++();

调用:

Counter& operator++()

c++;

编译器翻译成:

c.operator++(0);

调用:

Counter operator++(int)

这里的 0 是编译器自动传进去的占位参数。

new Derived 等于 new Derived() 吗?

大多数你现在遇到的类对象场景,可以先理解为:基本等价,都会调用默认构造函数

Derived* p1 = new Derived;Derived* p2 = new Derived();

如果 Derived 是类,并且有默认构造函数,二者都会构造一个 Derived 对象。

例如:

class Derived {public:    Derived() {        cout << "constructor" << endl;    }};Derived* p1 = new Derived;   // 调用 Derived()Derived* p2 = new Derived(); // 调用 Derived()

都会输出构造函数信息。

但对内置类型有区别:

int* p1 = new int;    // 未初始化,值不确定int* p2 = new int();  // 值初始化,得到 0

所以:

new int

得到不确定值;

new int()

得到 0

final 怎么理解?

作用 1:禁止类继续被继承

class A final {};

那么:

class B : public A {};

会报错。

意思是:

A 到此为止,不能再派生子类。


作用 2:禁止虚函数继续被重写

cpp
class Base {
public:    
	virtual void show() {}
};

class Derived : public Base {
public:
    void show() final {}
};

class MoreDerived : public Derived {
public: 
   void show() {} // 错误
};

意思是:

Derived::show() 是最后版本,后面的子类不能再 override。

更完整常见写法:

void show() override final {}

表示:

这是对基类虚函数的重写,并且禁止后续子类继续重写。

虚函数解决的是“用基类指针调用派生类函数”的问题

核心场景不是:

Derived* p = new Derived;p->show();

而是:

Base* p = new Derived;p->show();

因为实际工程里常常这么写:

vector<Base*> objects;objects.push_back(new Derived1);objects.push_back(new Derived2);objects.push_back(new Derived3);for (Base* p : objects) {    p->show();}

你希望虽然它们都被放在 Base* 里,但运行时能调用各自派生类的版本。这就需要 virtual

在继承中:

class Base {public:    void show() {}};class Derived : public Base {public:    void show(int x) {}};

Derived::show(int) 会隐藏 Base::show()

所以:

Derived d;d.show(1); // 正确,调用 Derived::show(int)d.show();  // 错误,Base::show() 被隐藏了

很多初学者会以为 d.show() 应该调用 Base::show(),但不会。因为只要派生类里出现了同名函数 show,基类的同名函数先被隐藏,不参与普通查找。

类型转换

const_cast

对象本身不是 const,只是被 const 指针/引用看到了:

int x = 10;const int* cp = &x;              // 通过 cp 看,不能修改int* p = const_cast<int*>(cp);   // 去掉 const*p = 20;                         // 合法,因为 x 本身不是 const

所以 const_cast 不是“让 const 对象变可改”,而是:

去掉表达式类型里的 const 限制。对象本身如果真是 const,仍然不能安全修改。