C++23 高级编程 Professional C++, Sixth Edition(一)
系列文章目录
文章目录
- 系列文章目录
- 前言
- 一、C++速成
- 二、string和 string_view
- 三、程序设计的一般方法
- 类之间的关系
- 设计可重用代码
前言
g++ -std=c++2b -o <executable_name> <source1.cpp> [source2.cpp ...]
-std=c++2b 表示启用对c++23功能的支持,当GCC完全兼容c++23后可改为 -std=c++23
-fmodules-ts 启用模块支持
编译标准库头文件<iostream>和<string>:
g++ -std=c++2b -fmodules-ts -xc++-system-header iostream
g++ -std=c++2b -fmodules-ts -xc++-system-header iostream
g++ -std=c++2b -fmodules-ts -xc++-system-header string
编译模块接口文件:
g++ -std=c++2b -fmodules-ts -c -x c++ AirlineTicket.cppm
编译应用代码:
g++ -std=c++2b -fmodules-ts -o AirlineTicket AirlineTicket.cpp AirlineTicketTest.cpp AirlineTicket.o
一、C++速成
// c++20
import std;
import <print>;std::println("hello this is {}", "rust");std::cout << std::format("Oh are {} ways I l u.", 219) << std::endl;
字面量:
整数,浮点数,字符,字符串
c++23中引入的扩展浮点数类型
类型 | 说明 | 字面量后缀 |
---|---|---|
std::float16_t | IEEE754 标准的16位格式 | F16, f16 |
std::float32_t | IEEE754 标准的32位格式 | F32, f32 |
std::float64_t | IEEE754 标准的64位格式 | F64, f64 |
std::float128_t | IEEE754 标准的128位格式 | F128, f128 |
std::bfloat16_t | 用于某些人工智能领域 | BF16, bf16 |
枚举
enum class PieceType: std::uint32_t {King = 1,Queen,Rook = 10,Pawn
};
PieceType piece { PieceType::King };
int underlying_value {std::to_underlying(piece)};
if-else
if ( <initializer>; <condi_expression>) {<if_body>
} else if (<else_if_expression>) {<else_if_body>
} else {<else_body>
}switch (<initializer>; <expression>) { <body> }case Custom:value = 84;[[fallthrough]]; // 告诉编译器某个 fallthrough 是有意为之// 阻止编译器发出警告
属性
指定内存对齐字节
#pragma pack(1) // 1字节内存对齐(必须是2的指数)
#pragma pack() // 默认对齐
- [[nodiscard]]
//可用于有一个返回值的函数,以使编译器在该函数被调用却没有对返回的值进行任何处理时发出警告
[[nodiscard]] int func() { return 42; }
// [[nodiscard("some explanation")]]func();
warning C4834: discarding return value of function with 'nodiscard' attribute
- [[maybe_unused]]
禁止编译器在未使用某些内容时发出警告
int func(int param1, [[maybe_unused]] int param2);
- [[noreturn]]
它永远不会将控制权返回给调用点
[[noreturn]] void force_terminate() {exit(1); // abort();
}
- [[deprecated]]
将某些内容标记为已弃用
[[deprecated("Unsafe function, please use xyz")]] void func();
- [[likely]], [[unlikely]]
int value { /* ... */ };
if (value > 11) [[unlikely]] { /* Do something ... */ }
else { /* Do something else ... */ }
switch (value)
{
[[likely]] case 1:
// Do something ...
break;
case 2:
// Do something ...
break;
[[unlikely]] case 12:
// Do something ...
break;
}
std::optional
#include <optional>
std::optional<int> get_data(bool giveit)
{if (giveit) return 43;return std::nullopt; // return {}
}std::optional<int> data1{ get_data(true) };
std::optional<int> data2{ get_data(false) };
if (data1) { data1.value(); }
if (data2.has_value()) { *data2; }
data2.value_or(0);
初始化列表
import std;
using namespace std;
int sum(initializer_list<int> values)
{int total { 0 };for (int value : values) {total += value;}return total;
}int a { sum({ 1, 2, 3 }) };
int b { sum({ 10, 20, 30, 40, 50, 60 }) };
作为面向对象语言的 C++
ticket.cppm
export module airline_ticket;import <string>;export class AirlineTicket
{public:AirlineTicket() = default;~AirlineTicket() = default;double calculate_price_in_dollars();std::string get_passenger_name();void set_passenger_name(const std::string& name);int get_number_of_miles();// void set_number_of_miles(int miles);bool has_elite_super_rewards_status();void set_has_elite_super_rewards_status(bool status);private:std::string passenger_{0};int number_{0};bool has_elite_{false};};int AirlineTicket::get_number_of_miles()
{return 1;
}double AirlineTicket::calculate_price_in_dollars()
{if (has_elite_super_rewards_status()) return 0;return get_number_of_miles() * 0.1;
}
std::string AirlineTicket::get_passenger_name()
{return passenger_;
}
void AirlineTicket::set_passenger_name(const std::string& name)
{passenger_ = name;
}
bool AirlineTicket::has_elite_super_rewards_status()
{return has_elite_;
}
void AirlineTicket::set_has_elite_super_rewards_status(bool status)
{has_elite_ = status;
}
g++ -std=c++23 -fmodules-ts -c -x c++ ticket.cppm
main.cpp
import <vector>;
import <print>;
import airline_ticket;
using namespace std;
int main() {vector v{1,2,3,4};println("size: {}", v);AirlineTicket al;al.set_passenger_name("david.smith");println("{}", al.get_passenger_name());}
const
1. const 修饰类型
const int* p;
int const* p;int * const p {nullptr};const int * const p {new int(4)};2. const成员函数
3.
结构化绑定
pair my_pair {"hello", 5};
auto [the_string, the_int] {my_pair};auto& [the_string, the_int] {my_pair};
const auto& [the_string, the_int] {my_pair};
auto
使用 auto 推断类型时去除了引用和 const 限定符。
int i {32};auto p {&i}; // int* p
const auto p1 {&i}; // int* const p1
auto const p2 {&i}; // int* const p2
const auto* p3 {&i}; // const int* p3
auto* const p4 {&i}; // int* const p4
const auto* const p5 {&i}; // const int* const p5;
decltype
未去除引用和const限定符
二、string和 string_view
c++20 三向比较运算符
string a {"ab"};
string b {"bc"};
auto result { a <=> b };
if (is_gt(result)) { println("greater"); }
if (is_lt(result)) { println("less"); }
if (is_eq(result)) { println("equal"); }
有用的转换函数
to_string()
stoi()
stol()
stoul()
stoll()
stoull()
class string_view
{
public:const char* ptr;size_t size;
};// string_view 字面量
using namespace std::literals::string_view_literals;
" this is string view\n"sv;inline constexpr basic_string_view<char>operator""sv(const char* __str, size_t __len) noexcept{ return basic_string_view<char>{__str, __len}; }
c++23后 format(), print(), println()的格式字符串必须是编译期常量,以便编译器可在编译期检查字符串中是否有任何语法错误。否则需要使用std::vprint_unicode()或 std::vprint_nonunicode()
int a = 89;
// 运行时可能抛出std::format_error异常
vprint_unicode(GetLocalizedFormat(language), make_format_args(a, a));
三、程序设计的一般方法
- 将程序划分为子系统,并捋清楚各子系统之间的关系
- 选择线程模型
- 指定每个子系统的类层次结构
- 指定每个子系统的类、数据结构、算法和模式
抽象:将接口和实现分离。从用户的观点确定接口,决定想让组件做什么,然后决定如何选择数据结构和算法。
重用: - 为每个子系统指定错误处理
类之间的关系
主要有两类:有一个(has a) ; 是一个(is a)
- 有一个
A有一个B,或者A包含一个B,某个类是另一个类的一部分,即某个类是另一个类的数据成员- 聚合: 通过聚合,当聚合器被销毁时,聚合对象(组件)可以继续存在。如,动物园和里面的动物。
- 组合:对于组合,如果由其他对象组成的对象被销毁时,那么这些其他对象也会被销毁。如何包含按钮的窗口对象被销毁,这些按钮对象也将被销毁。
- 是一个(继承)
优秀的面向对象层次结构:
- 使类之间存在有意义的功能联系
- 将共同的功能放入基类,从而支持代码重用
- 避免子类过多地重写父类的功能,除非父类是一个抽象基类
设计可重用代码
SOLID原则
SOLID | description |
---|---|
S | Single Responsibility Principle,单一职责原则。单个组件应当具有单独、明确定义的责任,不应当与无关的功能组合在一起 |
O | Open/Closed Principle,开放/关闭原则。一个类对扩展应当是开放的,但对修改时关闭的,可扩展、不修改已有实现 |
L | Liskov Substitution Principle,里氏替换原则。对于一个对象而言,应当能用该对象的子类型替代该对象的实例 |
I | Interface Segregation Principle,接口隔离原则。接口应当简单清楚,不要使用过于宽泛的通用接口,最好使用多个较小的、明确定义的、责任单一的接口 |
D | Dependency Inversion Principle,依赖倒置原则。使用接口倒置依赖关系。依赖注入是支持依赖倒置原则的一种方式 |