文章阅读
c++基础之cassert,cfloat,climits,codecvt,cwchar,local,memory,new,random
2025/12/1676 次阅读22 分钟
cassert - 断言
#include <cassert>
#include <iostream>
/**
* 断言
* false: 程序中止,程序中止执行
*/
int main() {
int a = 7;
int b = 2;
assert(a > b);// true:继续正常执行
std::cout << "a需要大于b" << std::endl;
//高级用法
int x = 1;
int y = 2;
assert(y == 0 && "y需要=0");
std::cout << "结束." << std::endl;
return 0;
}
输出
$ ./cassert
a需要大于b
Assertion failed: (y == 0 && "y需要=0"), function main, file cassert.cpp, line 16.
[1] 74755 abort ./cassert
cfloat - 浮点数
#include <iostream>
#include <string>
#include <cfloat>
#include <cmath>
using namespace std;
/**
* 浮点数
* float: 单精度浮点数,占用4字节
* double: 双精度浮点数,占用8个字节
*/
int main() {
float f = 3.14f;
double d = 2.334;
///输出float范围和精度
cout << "float:\
";
cout << "Min:" << FLT_MIN << "\
";
cout << "Max:" << FLT_MAX << "\
";
cout << "Epsilon:" << FLT_EPSILON << "\
";
cout << "Digits:" << FLT_DIG << "\
";
///double
cout << "\
double:\
";
cout << "min:" << DBL_MIN << "\
";
cout << "max:" << DBL_MAX << "\
";
cout << "epsilon:" << DBL_EPSILON << "\
";
cout << "digits:" << DBL_DIG << "\
";
/// long double
cout << "\
long double:\
";
cout << "min:" << LDBL_MIN << "\
";
cout << "max:" << LDBL_MAX << "\
";
cout << "epsilon:" << LDBL_EPSILON << "\
";
cout << "digits:" << LDBL_DIG << "\
";
/**
* float:
Min:1.17549e-38
Max:3.40282e+38
Epsilon:1.19209e-07
Digits:6
double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15
long double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15
*/
///使用cmath函数来操作
double num = 9.0;
double root = sqrt(num);//求平方根
double power = pow(2.0, 3.0);//计算2的3次幂
cout << "root is :" << root << endl; // =3
cout << "power is : " << power << endl;//=8
return 0;
}
输出
$ ./cfloat
float:
Min:1.17549e-38
Max:3.40282e+38
Epsilon:1.19209e-07
Digits:6
double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15
long double:
min:2.22507e-308
max:1.79769e+308
epsilon:2.22045e-16
digits:15
root is :3
power is 8
climits - 限制
#include <iostream>
#include <climits>
using namespace std;
/**
* 这个是和整数相关的限制和特性
*
*/
int main() {
cout << "int最大值:" << INT_MAX << endl;
cout << "int最小值:" << INT_MIN << endl;
cout << "long最大值:" << LONG_MAX << endl;
cout << "long最小值:" << LONG_MIN << endl;
cout << "unsigned long最大值:" << ULONG_MAX << endl;
cout << "char 最小值:" << CHAR_MIN << endl;
cout << "char 最大值:" << CHAR_MAX << endl;
cout << "char bit 位" << CHAR_BIT << endl;
/**
* int最大值:2147483647
int最小值:-2147483648
long最大值:9223372036854775807
long最小值:-9223372036854775808
unsigned long最大值:18446744073709551615
char 最小值:-128
char 最大值:127
char bit 位8
*/
return 0;
}
输出
$ ./climits
int最大值:2147483647
int最小值:-2147483648
long最大值:9223372036854775807
long最小值:-9223372036854775808
unsigned long最大值:18446744073709551615
char 最小值:-128
char 最大值:127
char bit 位8
codecvt - 字符转换工具
#include <iostream>
#include <codecvt>
#include <locale>
#include <string>
using namespace std;
/**
* 字符转换工具
*/
int main() {
//基于名称的转换器
wstring_convert<codecvt_byname<wchar_t>> converter("zh_CN.UTF-8");
// wstring_convert<codecvt_utf8_utf16<wchar_t>> converter;
//原始字符串
string narrow_string = "梁典典";
//转换utf-16宽字符串
wstring wide_string = converter.from_bytes(narrow_string);
wcout << L"宽字符串:" << wide_string << endl;
//转回uft8字符串
string onverted_string = converter.to_bytes(wide_string);
cout << "转回来了:" << onverted_string << endl;
return 0;
}
输出
转回来了:梁典典
cwchar - 宽字符和宽字符串
#include <iostream>
#include <cwchar>
#include <locale>
using namespace std;
/**
* 提供处理宽字符wchar_t和宽字符串的函数
* 输入输出
* 内存操作
* 字符串操作
*
* wchar_t: 宽字符类型,存储宽字符
* wint_t: 存储宽字符函数的返回值
*
* fgetwc: 文件流读取宽字符
* fputwc: 向文件流写入宽字符
* fgetws: 文件流读取宽字符串
* fputws: 向文件流写入宽字符串
*
* 主要用途:
• 处理多字节编码字符集(例如 Unicode)。
• 开发国际化应用程序时,通过 wchar_t 处理各国语言的字符。
对于需要处理不同语言的应用,宽字符提供了更好的支持,能够处理比 ASCII 更多的字符编码。
*/
int main() {
setlocale(LC_ALL, "zh_CN.UTF-8");//设置本地化
const wchar_t* filename = L"示例.txt";
FILE* file = fopen("示例.txt", "w");
cout << "file:" << file << endl;
if (file) {
fputws(L"你好,梁典典!\
", file);
fclose(file);
}
wprintf(L"测试");
file = fopen("示例.txt", "r");
cout << "读文件:" << file << endl;
if (file) {
wchar_t buffer[256];
if (fgetws(buffer, 256, file)) {
wcout << "从文件中读取" << buffer << "." << endl;
}
else {
wcout << L"读取失败了." << endl;
}
fclose(file);
}
else {
cout << "读文件失败." << endl;
}
///这里读取不到文件,不知道啥情况
/**
* 宽字符和宽字符串操作
* wcscpy: 拷贝宽字符串
* wcslen,获取宽字符串的长度
* wcscmp: 比较宽字符串
* wcsncpy: 拷贝指定长度的宽字符串
*/
wchar_t str1[100] = L"梁典典";
wchar_t str2[100] = L"你好";
///宽字符串拷贝
wcscpy(str1, L"你好啊梁典典");
wcout << L"拷贝狂字符串:" << str1 << endl;
///宽字符串长度
size_t len = wcslen(str1);
wcout << L"长度:" << len << endl;
///比较
int result = wcscmp(str1, str2);
wcout << L"比较结果:" << result << endl;
///部分拷贝,
wcsncpy(str2, str1, 5);
str2[5] = L'\0';
wcout << "部分字符串拷贝:" << str2 << endl;
/**
* 宽字符分类和转换
*/
//拍判断是不是字母
wchar_t ch = L'A';
if (iswalpha(ch)) {
wcout << ch << L"是字母" << endl;
}
//判断是不是数字
ch = L'9';
if (iswdigit(ch)) {
wcout << ch << L"是数组" << endl;
}
//转小写
ch = L'G';
wchar_t lower_ch = towlower(ch);
wcout << L"转写:" << lower_ch << endl;
ch = L'g';
wchar_t upper_ch = towupper(ch);
wcout << L"转大写:" << upper_ch << endl;
/**
* 宽字符和宽字符串的输入输出
*/
wchar_t buffer[100];
wprintf(L"格式化输出:%d %s\
", 42, L"梁典典");
//格式化输入
wprintf(L"输入数字和字符串");
wscanf(L"%d %ls", &buffer);
wprintf(L"输入的是:%ls\
", buffer);
//格式化宽字符写入宽字符串
swprintf(buffer, 100, L"格式话:%d %s", 42, L"梁典典");
wcout << L"buffer:" << buffer << endl;
//从宽字符串中读取格式化宽字符
int number;
wchar_t word[100];
swscanf(buffer, L"格式化:%d %s", &number, word);
wcout << "转换后:" << number << L"字符:" << word << endl;
return 0;
}
输出
file:0x1ed2b3ad0
测试读文件:0x1ed2b3ad0
格式化输出:42输入数字和字符串1
local - 国际化
#include <iostream>
#include <locale>
#include <string>
#include <ctime>
using namespace std;
/**
* 国际化
*/
int main() {
//创建默认的
locale loc;
// 使用locle对象
cout.imbue(loc);//设置cout的locale
//显示当前的locale名称
cout << "current locale" << loc.name() << endl;
//输出current localeC
// 使用locale格式化数字
locale loc2("en_US.UTF-8");//设置美国英语
cout.imbue(loc2);//设置cout的locale
double number = 1234.7890;
cout << "formatted number is " << number << endl;
//输出:formatted number is 1,234.79
/**
* 比较字符串
*/
locale loc3("en_US.UTF-8");
string str1 = "apple";
string str2 = "banana";
bool c1 = use_facet<collate<char>>(loc3).compare(str1.c_str(), str1.c_str() + str1.size(),
str2.c_str(), str2.c_str() + str2.size());
if (c1 < 0) {
cout << str1 << " comes before " << str2 << endl;
}
else {
cout << str1 << " comes after " << str2 << endl;
}
///输出:apple comes after banana
/**
* 日期和时间格式化
*/
locale loc4("en_US.UTF-8");
cout.imbue(loc4);
time_t now = time(nullptr);
tm* timeinfo = localtime(&now);
char buffer[100];
strftime(buffer, sizeof(buffer), "%A, %B, %d %Y", timeinfo);
cout << "current date: " << buffer << endl;
///current date: Friday, September, 13 2024
return 0;
}
输出
current localeC
formatted number is 1,234.79
apple comes after banana
current date: Wednesday, September, 18 2024
memory - 内存管理
#include <iostream>
#include <memory>
using namespace std;
/**
* 内存管理库
* c++11新特性
* 智能指针的主要类型:
* unique_ptr: 独占所有权的智能指针,同一个时间只有有一个unique_ptr指向特定内存
* shared_ptr: 共享所有权的智能指针,多个shared_ptr可以指向同一个内存,内存在最后一个shared_ptr被销毁时释放
* weak_ptr: 弱引用智能指针,用于shared_ptr配合使用,避免循环引用导致的内存泄露
*/
class MyClass {
public:
void doSomething() {
cout << "dosomethiing" << endl;
}
};
class Node {
public:
shared_ptr<Node> next;
weak_ptr<Node> prev;
Node() :next(nullptr), prev() {}
};
int main() {
unique_ptr<MyClass> myPtr(new MyClass());
myPtr->doSomething();//使用智能指针调用成员函数
//当main函数结束的时候,myPtr被销毁,自动释放MyClass的内存
/**
* 使用shared_ptr
*/
shared_ptr<MyClass> obj1(new MyClass());
shared_ptr<MyClass> obj2 = obj1;
obj1->doSomething();
obj2->doSomething();
//当obj1,obj2都被销毁时,myclass对象的内存被释放
/**
* weak_ptr
* 通常不单独使用,而是和shared_ptr结合使用,解决循环引用的问题
*/
/**
* 创建了两个节点 node1 和 node2,它们都被管理在 std::shared_ptr 中。
node1->next = node2;: node1 的 next 指针指向 node2,这意味着 node1 持有 node2 的所有权。
node2->prev = node1;: node2 的 prev 是一个 weak_ptr,指向 node1,但并不持有 node1 的所有权。
*/
shared_ptr<Node> n1 = make_shared<Node>();
shared_ptr<Node> n2 = make_shared<Node>();
n1->next = n2;
n2->prev = n1;
//循环引用,使用weak_ptr避免了内存泄露
/**
* 分配器: 提供了基本的内存分配和释放功能
*/
allocator<int> alloc;
int* p = alloc.allocate(1);//分配内存
alloc.construct(p, 2);//构造对象
std::cout << *p << std::endl;
alloc.destroy(p); // 销毁对象
alloc.deallocate(p, 1); // 释放内存
cout << "end" << endl;
/**
* align: 调整指针的对齐方式
*/
alignas(16) char buffer2[64];
void* p2 = buffer2;
size_t space = sizeof(buffer2);
void* aligned_ptr = align(16, sizeof(int), p2, space);
if (aligned_ptr) {
cout << "内存对齐" << endl;
}
else {
cout << "对齐失败" << endl;
}
return 0;
}
输出
$ ./memory
dosomethiing
dosomethiing
dosomethiing
2
end
new - 内存分配
#include <iostream>
#include <new> //头文件
using namespace std;
class MyClass {
public:
int value;
MyClass() : value(0) {}
};
int main() {
/**
* 动态分配单个对象
*/
MyClass* obj = new MyClass; //分配一个 myclass对象
obj->value = 10; //使用箭头操作符访问成员
cout << "value is " << obj->value << endl;
delete obj; //释放内存
/**
* 动态分配数组
*/
int* arr = new int[10]; //分配一个包含10个整数的数组
for (int i = 0; i < 10; ++i) {
arr[i] = i * 2;//初始化数组
}
for (int i = 0; i < 10; ++i) {
cout << "arr[" << i << "] = " << arr[i] << endl;
}
delete[] arr;
/**
* 使用nothrow避免异常
*/
int* arr2 = new(nothrow) int[10000000000000000];
if (!arr2) {
cout << "内存分配失败" << endl;
}
else {
cout << "内存分配成功" << endl;
delete[] arr2;
}
/**
* 异常处理
* 内存分配失败的时候,会抛出一个std::bad_alloc异常
* 使用try-catch来捕获异常
*/
try {
int* arr3 = new int[10000000000000000];
cout << "内存分配成功:arr3" << endl;
delete[] arr3;
}
catch (const std::bad_alloc& e) {
cout << "捕获到内存分配失败" << e.what() << endl;
}
return 0;
}
$ ./new
value is 10
arr[0] = 0
arr[1] = 2
arr[2] = 4
arr[3] = 6
arr[4] = 8
arr[5] = 10
arr[6] = 12
arr[7] = 14
arr[8] = 16
arr[9] = 18
内存分配失败
捕获到内存分配失败std::bad_alloc
random - 随机数
#include <iostream>
#include <random>
#include <iomanip>
#include <chrono>
using namespace std;
using namespace std::chrono;
/**
* 生成随机数的工具
*
*/
int main() {
//方式1: 使用random_device 生成种子
random_device rd;
//方式2: 使用当前时间作为种子
unsigned seed = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
///使用基于 mersenne twister 随机数生成器,不传种子会固定生成
mt19937 generator(rd());
cout << "random number:" << generator() << endl;
//输出random number:3499211612 (备注,每次输出都是一样的...)
/**
* 使用均匀分布
*/
mt19937 generator2(rd());
//设置范围
uniform_int_distribution<int> distribution(1, 10);
for (int i = 0; i < 5;i++) {
cout << "随机数:" << distribution(generator2) << endl;
}
/*
输出:
随机数:7
随机数:10
随机数:6
随机数:2
随机数:4
*/
/**
* 使用正态分布的随机数
*
*/
mt19937 generator3(seed);//使用当前时间作为种子
//创建正态分布的随机数,均值为0,标准差为1
normal_distribution<double> distribution2(0.0, 1.0);
//设置输出格式,保留2位小数
cout << fixed << std::setprecision(2);
//生成
for (int i = 0; i < 5; i++) {
cout << "随机数:" << distribution2(generator3) << endl;
}
/*
输出
随机数:-0.15
随机数:0.13
随机数:-1.87
随机数:0.46
随机数:-0.21
*/
return 0;
}
输出
$ ./random
random number:2805560704
随机数:7
随机数:7
随机数:6
随机数:3
随机数:5
随机数:0.35
随机数:0.44
随机数:0.71
随机数:-0.34
随机数:-0.91
utility - 实用工具类和函数
#include <iostream>
#include <utility>
#include <vector>
#include <type_traits>
using namespace std;
void process(int& i) {
cout << "左值引用 i :" << i << endl;
}
void process(int&& i) {
cout << "右值引用 i :" << i << endl;
}
template <typename T>
void forward_example(T&& t) {
process(std::forward<T>(t));
}
struct MyClass
{
MyClass(int, double) {}
};
template <typename T>
void test() {
//获取T的构造函数类型,不调用 (注意导包#include <type_traits>.)
using R = decltype(T(std::declval<int>(), std::declval<double>()));
cout << is_same<R, MyClass>::value << endl;
}
/**
包含了实用的工具类和函数:
pair: 模板类,存储2个不一样类型的值
make_pair:函数模板,创建pair对象,
swap: 函数模板,交换两个对象的值
*/
int main() {
/*
使用pair,和maake_pair
*/
auto p = make_pair(10, 20);
cout << "firset value is " << p.first << endl;
cout << "second value is " << p.second << endl;
/**
* swap
*
*/
int a = 1;
int b = 2;
cout << "初始化值,a=" << a << ",b=" << b << endl;
swap(a, b);//交换
cout << "交换后,a=" << a << ",b=" << b << endl;
/**
* move函数
*/
vector<int> v1 = { 1,2,3,4,5,6,7,8 };
vector<int> v2 = std::move(v1);
cout << "v1 size: " << v1.size() << endl;//空的
cout << "v2 size:" << v2.size() << endl; //8
/**
* forward函数
* std::forward 是 C++ 中一个模板函数,主要用于完美转发(perfect forwarding)场景。它的作用是根据传递的参数类型(左值或右值)将参数以相同的类型进行转发,避免不必要的拷贝或移动操作。
关键概念
为了理解 std::forward,你需要先了解几个相关概念:
1. 左值和右值:左值(lvalue)是一个可以取地址的对象,右值(rvalue)是临时的值,通常在表达式中出现,如字面常量或返回的临时对象。
2. 完美转发:在泛型编程中,你希望函数模板能够保持参数的原始类型(左值或右值)并正确地将其传递给其他函数。std::forward 就是用于在这种情况下保持参数的值类别。
std::forward 的作用
当你想要在函数模板中保持参数的左值或右值特性时,std::forward 能根据传递的参数类型正确地转发:
• 如果传递的是左值,std::forward 会保留其左值特性。
• 如果传递的是右值,std::forward 会保留其右值特性。
*/
int x = 10;
forward_example(x); //左值
forward_example(20); //右值
forward_example(std::move(x)); //右值
/**
* declval
*/
test<MyClass>();
return 0;
}
输出
$ ./utility
firset value is 10
second value is 20
初始化值,a=1,b=2
交换后,a=2,b=1
v1 size: 0
v2 size:8
左值引用 i :10
右值引用 i :20
右值引用 i :10