收集一下奇奇怪怪的玩意(包括冷门用法)。。。。

虚函数的默认参数

#include <iostream>
using namespace std;

class Base {
public:
    virtual void show(int x = 10) = 0;

    virtual ~Base() { cout << "~Base\n"; }
};


class Derive : public Base {
public:
    void show(int x = 20) override {
        cout << "x = " << x << endl;
    }

    ~Derive() override { cout << "~Derive\n"; }
};

int main(int argc, char* argv[]) {
    Base* bb = new Derive();
    bb->show();
    delete(bb);

    return 0;
}

c++ alignas 和 alignof

alignas 是 C++11 引入的对齐说明符,用于指定变量或类型的对齐要求。

基本用法

#include <iostream>
using namespace std;

// 指定结构体16字节对齐
struct alignas(16) AlignedStruct {
    char data[8];
};

int main() {
    AlignedStruct s;
    cout << "Alignment: " << alignof(AlignedStruct) << endl;
    cout << "Size: " << sizeof(AlignedStruct) << endl;
    
    return 0;
}

变量对齐

#include <iostream>
using namespace std;

int main() {
    // 指定变量对齐
    alignas(32) int array[10];
    alignas(64) char buffer[128];
    
    cout << "Array alignment: " << alignof(decltype(array)) << endl;
    cout << "Buffer alignment: " << alignof(decltype(buffer)) << endl;
    
    return 0;
}

类成员对齐

#include <iostream>
using namespace std;

class MyClass {
public:
    char c;
    alignas(8) int i;  // 这个成员8字节对齐
    double d;
};

int main() {
    MyClass obj;
    cout << "Class size: " << sizeof(MyClass) << endl;
    cout << "Class alignment: " << alignof(MyClass) << endl;
    
    return 0;
}

cout 的缓冲区

std::cout 默认是有缓冲的。如果你在写高性能代码,不理解缓冲就是在浪费 CPU 周期。

1. 缓冲模式

  • 全缓冲 (Fully Buffered):缓冲区满了才输出。通常在 cout 被重定向到文件时发生。
  • 行缓冲 (Line Buffered):遇到换行符 \n 就输出。这是 cout 连接到终端时的默认行为。
  • 无缓冲 (Unbuffered):数据直接输出。std::cerr 默认就是这种模式,因为错误信息需要立即显示。

2. endl vs \n

这是新手最容易犯错的地方:

  • std::endl:输出 \n 并强制调用 flush()。在循环里用 endl 会导致频繁的系统调用,性能烂得像坨屎。
  • \n:只输出换行符。让缓冲区自己决定什么时候该刷新。

如果你不需要立即看到输出(比如在跑一个几百万次的循环),永远用 \n

3. 性能优化

如果你觉得 C++ 的 I/O 慢,通常是因为它在同步老旧的 C stdio

std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);

这两行代码能让你的 I/O 速度提升一个量级,但你需要明白它们在干什么:

  • sync_with_stdio(false):默认情况下,C++ 的 cout 要和 C 的 printf 同步,共享同一个缓冲区。这就像是两个人共用一个笔记本,每次写字都要互相打招呼,慢得要死。关掉它,cout 就能拥有自己的独立缓冲区,跑得飞快。代价:你不能再在同一个程序里混用 coutprintf,否则输出顺序会乱套。
  • cin.tie(nullptr):默认情况下 cincout 是绑定的。这意味着每次你从 cin 读数据前,cout 都会被强制刷新(flush)。这是为了保证你在输入前能看到提示语(比如 “请输入:”)。但在处理大量数据时,这种频繁的自动刷新就是性能杀手。解绑后,cout 只有在缓冲区满或者你手动刷新时才会输出。

4. std::cout vs C++23 std::print

C++23 引入了 <print>,这才是现代 C++ 该有的样子。

  • 类型安全与格式化cout 靠一堆 << 拼接,代码又臭又长。std::print 使用类似 std::format 的语法,类型安全且可读性极高。
  • 性能std::print 通常比 std::cout 快。它直接将格式化后的字符串交给底层系统调用,避免了 ostream 那套复杂的内部状态维护和多次缓冲区拷贝。
  • 原子性:在多线程环境下,多个线程同时用 cout 输出会导致字符交织(除非你加锁)。std::print 的单次调用通常是原子的,输出不会被打碎。
  • Unicode 支持std::print 原生支持 UTF-8 输出到终端,而 cout 在 Windows 等平台上处理 Unicode 就是个噩梦。

结论:如果你的编译器支持 C++23,赶紧把那堆垃圾 cout 换成 std::print

operator"" 用户自定义字面量

C++11 引入了用户自定义字面量(User-defined literals),通过 operator"" 让你定义自己的字面量后缀。这玩意儿能让代码读起来像自然语言,但用不好就是灾难。

基本语法

#include <iostream>
using namespace std;

// 整型字面量后缀
constexpr long long operator"" _KB(unsigned long long value) {
    return value * 1024;
}

// 浮点型字面量后缀
constexpr long double operator"" _deg(long double degrees) {
    return degrees * 3.14159265358979323846L / 180.0;
}

// 字符串字面量后缀
string operator"" _str(const char* str, size_t len) {
    return string(str, len);
}

int main() {
    auto size = 4_KB;  // 4096
    auto angle = 90.0_deg;  // 1.5708...
    auto text = "hello"_str;  // std::string

    cout << "Size: " << size << endl;
    cout << "Angle: " << angle << endl;
    cout << "Text: " << text << endl;

    return 0;
}

参数类型规则

operator"" 的参数类型由字面量类型决定,这是编译器强制的:

字面量类型参数类型示例
整型unsigned long long123_suffix
浮点型long double3.14_suffix
字符串(const char*, size_t)"abc"_suffix
字符char'a'_suffix
#include <iostream>
using namespace std;

// 字符字面量后缀
char operator"" _upper(char c) {
    return (c >= 'a' && c <= 'z') ? c - 32 : c;
}

int main() {
    cout << 'a'_upper << endl;  // 'A'
    cout << 'Z'_upper << endl;  // 'Z'

    return 0;
}

实际应用场景

1. 类型安全的单位系统

#include <iostream>
using namespace std;

struct Distance {
    long double meters;
    constexpr Distance(long double m) : meters(m) {}
};

constexpr Distance operator"" _m(long double value) {
    return Distance(value);
}

constexpr Distance operator"" _km(long double value) {
    return Distance(value * 1000);
}

constexpr Distance operator"" _cm(long double value) {
    return Distance(value / 100);
}

int main() {
    auto d1 = 1.5_m;
    auto d2 = 2.0_km;
    auto d3 = 150.0_cm;

    cout << "d1: " << d1.meters << " m" << endl;
    cout << "d2: " << d2.meters << " m" << endl;
    cout << "d3: " << d3.meters << " m" << endl;

    return 0;
}

2. 编译期字符串哈希

#include <iostream>
using namespace std;

// FNV-1a 哈希算法
constexpr unsigned long long operator"" _hash(const char* str, size_t len) {
    unsigned long long hash = 14695981039346656037ULL;
    for (size_t i = 0; i < len; ++i) {
        hash ^= static_cast<unsigned long long>(str[i]);
        hash *= 1099511628211ULL;
    }
    return hash;
}

int main() {
    constexpr auto h1 = "hello"_hash;
    constexpr auto h2 = "world"_hash;

    cout << "hash(\"hello\"): " << h1 << endl;
    cout << "hash(\"world\"): " << h2 << endl;

    // 编译期计算,零运行时开销
    static_assert("test"_hash == 13582463669883289057ULL, "Hash mismatch");

    return 0;
}

3. 时间字面量

#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;

constexpr seconds operator"" _s(unsigned long long sec) {
    return seconds(sec);
}

constexpr milliseconds operator"" _ms(unsigned long long ms) {
    return milliseconds(ms);
}

constexpr microseconds operator"" _us(unsigned long long us) {
    return microseconds(us);
}

int main() {
    auto timeout = 5_s;
    auto delay = 100_ms;
    auto interval = 500_us;

    cout << "Timeout: " << timeout.count() << " seconds" << endl;
    cout << "Delay: " << delay.count() << " milliseconds" << endl;
    cout << "Interval: " << interval.count() << " microseconds" << endl;

    return 0;
}

命名空间限制

用户自定义字面量必须在命名空间中定义,不能在全局作用域(除非是标准库的)。这是为了避免污染全局命名空间。

#include <iostream>
using namespace std;

namespace my_literals {
    constexpr int operator"" _bin(const char* str, size_t len) {
        int value = 0;
        for (size_t i = 0; i < len; ++i) {
            value = value * 2 + (str[i] - '0');
        }
        return value;
    }
}

int main() {
    using namespace my_literals;
    cout << "1010"_bin << endl;  // 10

    return 0;
}

注意事项

  1. 后缀必须以下划线开头:以下划线开头的后缀保留给用户使用,不以 _ 开头的保留给标准库。
  2. 编译期计算:如果标记为 constexpr,字面量可以在编译期计算,零运行时开销。
  3. 类型安全:相比宏,用户自定义字面量是类型安全的,编译器会进行类型检查。
  4. 不要滥用:这玩意儿用得好是神器,用不好就是灾难。只在你真的需要的时候用,别为了炫技而用。