关于stl的使用 他真的这么好用嘛?他性能真的那么高效嘛?
是的 google 都在使用c++17的新特性 google是对性能精益求精的公司 连头部大手子都在使用的c++17新特性 你有什么理由不去使用他?性能就不用多说了 在优化拉满 release版的情况下 性能无限接近纯原生系统调用 你有啥理由不使用 下面让我们来看看c++17增加哪些新东西 为什么要增加这些新东西 他解决了什么问题 以及如何使用他
Tips:本人观点:光是去关注c++标准委员会和Google的动向 你都能学到很多东西 你能大概知道目前大家遇到的问题是啥 以及c++标准委员 Google 会如何解决他 比如c++23将要推出的协程 Google的grpc 等等
前言
关于c++14的新特性 个人觉得和c++11差距并不是太大 也就不细说了 也许日后会专门写篇文章来写c++14的新特性
c++17之string_view
为什么要有string_view
string有一个最大的缺点 就是他必须拥有字符串资源的所有权 这是什么意思呢 当下段代码发生的时候其实是发生了拷贝 如下图所示
std::string mystring = "this is play";
"this is play"这个字符串是放在常量区的 但是在构造string的时候 他会把这个字符串复制到堆区或者是栈区来自己管理 c++程序员是很讨厌拷贝的 为了解决这个问题 所以c++17推出了string_view 他就是不会去拷贝常量区资源 而是直接指向他 相当于就是普通的const char*和一个记录字符串大小的字段size
什么情况下使用string_view
当我们不想去拷贝常量区资源 也就是我们并不想拥有字符串资源的所有权的时候
怎样使用string_view
当我们使用string_view的时候 就可以这么玩 非常的简单 下图并没有发生拷贝
std::string_view mystring = "this is play";
并不想发生拷贝的场景有点多哦 具体怎么玩就要看自己的选择咯 这个是确实能够优化性能的东西
c++17之any
为什么要有any?
在c++17之前我们想用一种类型来表示任何一种资源的时候 常用的方法如下
void* p = new std::vector<int>();
void* p1 = new std::map<int, int>();
std::vector<void*> anytype_vec;
anytype_vec.push_back(p);
anytype_vec.push_back(p1);
我们使用void * 型的指针来表示任何一种资源 但是这个方式往往是不安全的 因为当你使用的时候你的使用方式如下
void* p = new std::vector<int>();
void* p1 = new std::map<int, int>();
std::vector<void*> anytype_vec;
anytype_vec.push_back(p);
anytype_vec.push_back(p1);
std::vector<int>* p2=(std::vector<int>*)anytype_vec.front();
p2->push_back(1);
编译器并不会执行任何的安全检查 当你玩出事的时候 因为void*并不能给你存储原类型信息 所以默认所有的强制转换都是允许的 比如在你写代码的时候你已经彻底遗忘了当时塞进去的东西到底是啥而乱用了转换 直接会导致程序崩溃 而不是给你任何提示
所以c++17为了安全 建议不要使用void*的这种方式来表示任意类型 而是使用std::any来表示 上述代码优化后如下所示
std::vector<int>p();
std::map<int, int>p1(1,1);
std::vector<std::any> anytype_vec;
anytype_vec.push_back(p);
anytype_vec.push_back(p1);
std::vector<int>&p2 = any_cast<std::vector<int>&>(anytype_vec.front());
p2.push_back(1);
什么情况下使用std::any
也就是上图 当我们想表示任意数据类型的时候 就可以使用 当然性能是肯定不如void*这种方式的 你要是能完全把握的住你的代码 当然可以随便使用void*这种方式去表示任意类型
怎样使用std::any
来看一个最简单的例子如下
std::any testFunc()
{
if ("someting")
{
return std::vector<int>();
}
else
{
return std::map<int, int>();
}
}
这段代码100%是可以编译通过的 可以看到std::any就可以接受任意数据类型 接下来我们看看他是如何保证安全的 在真正使用的时候你需要使用any_cast来进行强转 如果强转失败便会抛出bad_any_cast的异常 代码使用如下
std::any testFunc()
{
if ("someting")
{
return std::vector<int>();
}
else
{
return std::map<int, int>();
}
}
int main()
{
try
{
any_cast<std::map<int,int>>(testFunc());
}
catch (std::exception& e)
{
std::cout << e.what() << std::endl;
}
}
运行结果如下图
Tips:这个东西主要是为了安全才提出的 如果你对自己的代码非常有信心 当然可以不用啦 如果你把握的住 不想考虑安全 当然就使用原生的C语言的方式去写当然更快而且更牛逼
c++17之optional
记得我第一次见到optional的时候是在Google的protobuf的代码里 当时就不太理解这个东西的作用 所以这里一起整理一下
为什么要有optional
想象一下这个场景 我们想通过一个工厂函数来构造一个对象 构造成功我们希望在栈上返回 有可能构造失败 构造失败我们希望返回一个空 而不希望去调用它的构造函数 马上你就会发现这在c++17之前是似乎是无法做到的 读者如果不信的话 可以自己去试试 为了解决这种问题 c++17推出了optional
什么情况下使用optional
当你想在栈上返回对象 同时希望构造失败的时候不去调用它的构造函数 而是直接返回空时
如何使用optional
代码如下图所示
class Person {
public:
int i{};
};
std::optional<Person> testFunc(int size)
{
if (size==0)
{
return std::nullopt;
}
else
{
return Person();
}
}
int main()
{
auto p = testFunc(0);
if (p == nullopt)
{
//do someting
std::cout << "construct failure" << std::endl;
}
else
{
std::cout << p->i << std::endl;
}
}
运行结果如下
当我们把testFunc的参数改为1的时候 运行结果如下
我们成功的在栈上返回了对象 并且当构造失败的时候我们并没有去调用到构造函数
c++17之shared_mutex
为什么要有shared_mutex
c++ stl里一直都没有对原生的读写锁的支持 如今这个东西终于做到了跨平台并且加入到了stl中 读写锁具体有什么用学过并发编程的都知道 在某些场景下 我们就是希望有多个同时读而同时只能有一个写 因为读并不会改变他的值 所以是线程安全的
什么情况下使用shared_mutex
多线程并发场景下 当你想使用读写锁的时候 就使用shared_mutex
如何使用shared_mutex
下面我们来看一个最典型的例子
class use_count
{
public:
int count{};
std::shared_mutex myLock{};
int readCount()
{
std::shared_lock ding(myLock);
std::cout << "nice read" << std::endl;
return count;
}
bool writeCount(int i)
{
std::unique_lock ding(myLock);
count += i;
std::cout << "nice write" << std::endl;
return true;
}
};
void readLoop(use_count*p)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
for(int i=0;i<10000;i++)
p->readCount();
}
void writeLoop(use_count* p)
{
for(int i=0;i<100000;i++)
p->writeCount(1);
}
int main()
{
use_count p{};
std::thread ding1(readLoop,&p);
std::thread ding2(readLoop, &p);
std::thread ding3(readLoop, &p);
std::thread ding4(readLoop, &p);
std::thread ding5(readLoop, &p);
std::thread ding6(writeLoop,&p);
ding1.join();
ding2.join();
ding3.join();
ding4.join();
ding5.join();
ding6.join();
std::cout << "total count is" << p.count;
}
程序运行结果:
我只是大概表达一下我的意思 具体什么情况下用什么锁以及到底咋用 你自己研究并发编程去吧
c++17之 try_emplace
这个我是真不想说为什么要有了 大概说一下 因为普通的emplace是先构造再去检查其中有没有 而我们希望先检查有没有再构造 所以更快 代码如下
struct RValue {
public:
RValue() :sources("hello!!!") {
cout << "默认构造 " << endl;
}
RValue(const char* p):sources(p)
{
std::cout << "构造函数调用" << std::endl;
}
RValue(RValue&& a) {
sources = std::move(a.sources);
cout << "&& RValue" << endl;
}
void operator=(const RValue&& a) {
sources = std::move(a.sources);
cout << "&& ==" << endl;
}
bool operator<(const RValue& a)const
{
if(a.sources.size()<sources.size())
return true;
else
{
return false;
}
}
bool operator>(const RValue& a)const
{
if (a.sources.size() < sources.size())
return true;
else
{
return false;
}
}
bool operator==(const RValue& a)const
{
if (a.sources.size() == sources.size())
return true;
else
return false;
}
std::string sources;
};
int main()
{
std::map<RValue,RValue>ding;
std::cout << "emplace---------------" << std::endl;
ding.emplace("nice", "nice");
std::cout << "emplace---------------" << std::endl;
std::cout << "try_emplace---------------" << std::endl;
ding.try_emplace("nice", "nice");
std::cout << "try_emplace---------------" << std::endl;
std::cout << "emplace---------------" << std::endl;
ding.emplace("nice", "nice");
std::cout << "emplace---------------" << std::endl;
}
运行结果如下图所示 其他就不用多说了 直接看代码你应该就能懂
再分享两个个人觉得比较好用的两个语法糖 如下
1.if初始化语句
if (auto it = ding.find("nice"); it != ding.end())
{
//do someting
}
2.lambda 捕获对象成员变量 如下
int main()
{
RValue myRValue("good");
[nice = myRValue.sources] ()
{
std::cout << nice;
}();
}