C++ 基础学习5
std::variant
参考:https://blog.csdn.net/hang_ning/article/details/123826220
https://blog.csdn.net/janeqi1987/article/details/100568096
总结:std::variant代表了类型安全的union类型,与union类似,std::variant的一个实例要么存储了可选类型中的一个值,要么没有存储。但与union比,其优势是可以判断当前真实的存储类型并且可以存储结构体这种复杂的数据结构。
推荐使用std::get_if或者std::holds_alternative来判断std::variant变量保存的具体类型:
#include <variant>
using MyType value = std::variant<int, double, bool, std::string>;
MyType value = "hello, world";
if (std::holds_alternative<std::string>(va)) {
std::cout << "holds std::string" << std::endl;
} else {
std::cout << "Not holds std::string" << std::endl;
}
auto* v = std::get_if<std::string>(&value);
if (v){
std::cout<< *v <<std::end; //"hello, world"
}
注意:上面的代码是在运行期进行类型判断效率较低;应当尽量使用 std::visit 方法来访问,实现在编译期推断:
#include "overloaded.h"
std::variant<int, string> value = 123;
std::visit(overloaded{
void operator()(int i) { cout << "int: " << i << '\n'; }
void operator()(const std::string& s) { cout << "str: " << s << '\n'; }
},value);
std::visit(
[&](auto &&arg) {
using T = std::decay_t<decltype(arg)>; // 类型退化,去掉类型中的const 以及 &
if constexpr(std::is_same_v<T,int>) {
cout << "int: " << arg << '\n';
} else if constexpr(std::is_same_v<T,std::string>){
cout<< "str: "<< arg <<'\n';
}
}, value);
std::visit(Overloaded{
[&out](const int &val) { out << "GatewayParam{" << val << "} "; },
[&out](const string &val) { out << "ServiceParam{" << val << "} "; },
},
value );
但是使用std::visit也有限制条件:必须把每种数据类型的处理函数都写出来;如果你只想处理其中一种参数,且对运行性能没有极致要求,那么还是在运行时进行判断吧。否则会报如下的编译失败:
type_traits:2957:11: error: no type named ‘type’ in ‘struct xxx
其它方法:
std::remove、erase
https://blog.csdn.net/yedawei_1/article/details/108754282
std::remove()是C++ STL中的一个算法,用于从容器中删除元素。它不会真正地删除元素,而是将要删除的元素移动到容器的末尾,并返回一个指向新的逻辑结尾的迭代器。这个迭代器之前的所有元素都是未被删除的,而之后的所有元素都是已被删除的。要真正地删除这些元素,可以使用容器的erase()函数。
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {4, 1, 2, 4, 3, 4, 5};
auto new_end = std::remove(v.begin(), v.end(), 4);
v.erase(new_end, v.end());
for (auto i : v) {
std::cout << i << " ";
}
std::cout << std::endl;
}
输出:
1 2 3 5
std::string::erase
http://www.manongjc.com/detail/30-ozpmqwwlzpuodgn.html
可以实现删除单个字符、一段连续的字符、或者所有特定的字符。
比如,一个字符串格式如下:开头部分为"-----Begin-----\n",结尾部分为"-----End-----\n",中间除了正常内容外还有很多换行符’\n’,去掉开头部分、结尾部分、中间所有的\n:
std::string_view beginPart = "-----Begin-----\n";
std::string_view endPart = "-----End-----\n";
// 去掉BEGIN部分
std::size_t beginPos = s.find(beginPart);
if (beginPos != std::string::npos) {
s.erase(0, beginPart.length());
}
// 去掉End部分
std::size_t endPos = s.find(endPart);
if (endPos != std::string::npos) {
s.erase(endPos, endPart.length());
}
// 去掉所有的换行符
s.erase(std::remove(s.begin(), s.end(), '\n'), s.end());
std::filesystem
https://blog.csdn.net/chenyijun/article/details/109263942
std::filesystem文件系统库提供了文件系统、路径、常规文件、目录等等相关组件进行操作的相关功能。
void path_use()
{
fs::path currentPath = fs::current_path();
//获取makefile路径 append operator/ 添加元素到带目录分隔符的路径
fs::path makefilePath = currentPath / "Makefile.Debug"; // 路径拼接
std::cout << "Makefile path = " << makefilePath.string() << std::endl;
//分解操作
//root_name 返回路径的根名
std::cout << "root_name = " << currentPath.root_name() << std::endl;
//root_directory 返回路径的根目录
std::cout << "root_directory = " << currentPath.root_directory() << std::endl;
//root_path 返回路径的根路径
std::cout << "root_path = " << currentPath.root_path() << std::endl;
//relative_path 返回相对根路径的路径
std::cout << "relative_path = " << currentPath.relative_path() << std::endl;
//parent_path 返回亲路径的路径
std::cout << "parent_path = " << currentPath.parent_path() << std::endl;
//filename 返回文件名路径组分
std::cout << "filename = " << currentPath.filename() << std::endl;
//stem 返回主干路径组分
std::cout << "stem = " << currentPath.stem() << std::endl;
//extension 返回文件扩展名路径组分
std::cout << "extension = " << currentPath.extension() << std::endl;
std::cout << "extension = " << makefilePath.extension() << std::endl;
//查询操作
//empty 检查路径是否为空
std::cout << "empty = " << currentPath.empty() << std::endl;
//检查对应路径元素是否非空
std::cout << "has_root_path = " << currentPath.has_root_path() << std::endl;
std::cout << "has_root_name = " << currentPath.has_root_name() << std::endl;
std::cout << "has_root_directory = " << currentPath.has_root_directory() << std::endl;
std::cout << "has_relative_path = " << currentPath.has_relative_path() << std::endl;
std::cout << "has_parent_path = " << currentPath.has_parent_path() << std::endl;
std::cout << "has_filename = " << currentPath.has_filename() << std::endl;
std::cout << "has_stem = " << currentPath.has_stem() << std::endl;
std::cout << "has_extension = " << currentPath.has_extension() << std::endl;
//检查 root_path() 是否唯一标识文件系统位置
std::cout << "is_absolute = " << currentPath.is_absolute() << std::endl;
std::cout << "is_relative = " << currentPath.is_relative() << std::endl;
}
const和constexpr的区别
const和constexpr都是C++中的关键字,但它们的用途不同。const用于定义常量,而constexpr用于定义常量表达式。
const关键字可以用于修饰变量、函数参数、函数返回值等,表示该变量或函数返回值不可修改。
const int a = 10;
const int *p = &a;
constexpr关键字可以用于定义常量表达式,即在编译时就能计算出结果的表达式。
constexpr int factorial(int n) {
return n <= 1 ? 1 : (n * factorial(n - 1));
}
coreDump和异常的区别
C++中core dump和异常的区别是,core dump是程序在被操作系统杀掉以后,保留的一份内存快照,一般有Segmentation Fault和Abort等。
异常是指程序在运行时发生了错误,但是程序可以通过catch语句捕获并处理这个错误。
C++ core dump调试及崩溃监控
https://blog.csdn.net/hello2mao/article/details/79258471
https://blog.csdn.net/Bruce_0712/article/details/72824957
(1) 保存core dump文件
ulimit -c unlimited
(2) 利用core文件定位、调试问题
gdb 可执行程序 core
(3) 动态库的core dump调试,在CMakeList.txt中添加:
add_definitions(-D_DEBUG)
add_definitions(-DDEBUG)
add_definitions("$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
(4) 崩溃监控方案
向系统注册发生Segmentation Fault和Abort时的回调即可监控C++的崩溃:
#include <signal.h> // ::signal
::signal(SIGSEGV, &my_signal_handler);
::signal(SIGABRT, &my_signal_handler);
在回调里就可采集崩溃栈等信息,可以使用boost库方便的记录backtrace等信息:
#include <signal.h> // ::signal, ::raise
#include <boost/stacktrace.hpp>
void my_signal_handler(int signum) {
::signal(signum, SIG_DFL);
boost::stacktrace::safe_dump_to("./backtrace.dump");
::raise(SIGABRT);
}
怎样让 C++ 中 throw exception 产生的 coredump 带上栈?
https://cloud.tencent.com/developer/article/1839207
如果是对已有的二进制,或者已经在运行的进程:
gdb 里面输入:
catch throw
然后运行, gdb 就会在任何 throw 的时候暂停,即可看到 throw 时候的栈。