c++11强化知识点
2020-11-15 本文已影响0人
拉普拉斯妖kk
初始化列表(std::initializer_list)
- c++11提供了std::initializer_list,将使得类对象的初始化也可以和普通数组或者POD数据一样使用初始化列表的方式。只要为类对象提供初始化列表构造函数即可。
- std::initializer_list也可以作为函数的参数使用。
- 初始化列表std::initializer_list相关示例代码如下。
#include <iostream>
#include <initializer_list>
#include <vector>
using namespace std;
class InitClass {
public:
InitClass(initializer_list<int> list) {
for (int l : list)
initializer_list_.emplace_back(l);
}
void PrintInit() {
for (int l : initializer_list_)
cout << l << endl;
}
void Print(initializer_list<int> list) {
for (int l : list)
cout << l << endl;
}
private:
vector<int> initializer_list_;
};
struct A {
double a;
int b;
};
struct B {
B(int a, double b): a_(a), b_(b) {}
private:
int a_;
double b_;
};
int main()
{
// 使用初始化列表初始化类对象
InitClass i = {1, 2, 3, 4, 5};
i.PrintInit();
cout << endl;
// 初始化列表做函数参数
i.Print({1, 2, 3});
// 使用初始化列表初始化POD数据
vector<int> v = {1, 2, 3, 4};
A a {1.1, 1};
B b {2, 2.2};
return 0;
}
变长参数模板(typename... Args)
- c++11提供了变长参数模板,堪比黑魔法。可以实现任意类型、任意个数的变长参数模板类和函数。
- 可以使用经典的递归模板函数的方式去取出变长参数模板函数中的参数,示例代码如下。
#include <iostream>
template<typename T>
void printf(T value) {
std::cout << value << std::endl;
}
template<typename T, typename... Args>
void printf(T value, Args... args) {
std::cout << value << std::endl;
printf(args...);
}
int main() {
printf(1, 2, "123", 1.1);
return 0;
}
- c++14提供了更简便的方法,可以使用初始化列表展开变长参数,示例代码如下。
// 编译这个代码需要开启 -std=c++14
#include <iostream>
template<typename T, typename... Args>
auto print(T value, Args... args) {
std::cout << value << std::endl;
return std::initializer_list<T>{([&] {
std::cout << args << std::endl;
}(), value)...};
}
int main() {
print(1, 2.1, "123");
return 0;
}
强类型枚举(enum class)
- c++11提供了类型安全的枚举类enum class。枚举类中定义的枚举值不能够被隐式转换为整数,也不能与整数直接比较,更不能与不同的枚举类型的枚举值比较。
- 枚举类定义的枚举值可以定义相同的值。
- 枚举类中可以自己定义枚举值的类型,默认是int。
- 可以通过重载<<运算符来实现直接打印枚举类的值。
- 可以定义模板转换函数来方便将枚举类的枚举值与基本数据类型(如int)直接进行比较。
- 所有示例代码如下。
#include <iostream>
enum class new_enum : unsigned int {
value1,
value2,
value3 = 888,
value4 = 888
};
template<typename T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e) {
return stream << static_cast<typename std::underlying_type<T>::type>(e);
}
template<typename T>
auto to_underlying_type(const T& e) {
return static_cast<typename std::underlying_type<T>::type>(e);
}
int main() {
if (new_enum::value3 == new_enum::value4 && 888 == to_underlying_type(new_enum::value3))
{
std::cout << new_enum::value3 << std::endl;
}
return 0;
}
函数对象包装器(std::function)
- c++11提供了可以定义任意可调用类型的函数对象包装器(std::function),这是对函数对象的一种类型安全的包装。
- 利用std::function和c++11提供的更强大的using语法以及Lambda函数,我们可以更方便的实现类对象中的简单的回调函数。示例代码如下。
#include <iostream>
#include <memory>
class A {
public:
using CallBack = std::function<void()>;
void SetCallBack(CallBack cb) { call_back_ = cb; }
void CallCallBack() {
if (call_back_)
{
call_back_();
}
}
private:
CallBack call_back_ = nullptr;
};
class B {
public:
B() {
a = std::make_shared<A>();
a->SetCallBack([](){
std::cout << "call A cb in B" << std::endl;
});
a->CallCallBack();
}
private:
std::shared_ptr<A> a = nullptr;
};
int main() {
B b;
}
- 如果回调函数很复杂,不适合用Lambda函数实现,也可以通过std::bind来将回调函数绑定到类的成员函数上。
智能指针初始化(make_unique)
- 为了简化c++的指针管理,c++11扩展了标准库,推出了智能指针——std::shared_ptr/std::unique_ptr/std::weak_ptr。智能指针使用引用计数的方式来实现自动释放资源,使得c++语言更具现代性。
- 标准库提供了std::make_shared来初始化一个std::shared_ptr,避免了我们使用new来初始化std::shared_ptr。但是由于“被标准委员会忘记了”,标准库却没有提供make_unique的方法来给我们初始化一个std::unique_ptr(好在c++14提供了)。为此我们可以自己实现一个make_unique,代码如下。
#include <memory>
template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args ) {
return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}
int main() {
std::unique_ptr<int> p = make_unique<int>(1);
return 0;
}