记录几点C++11特性的简单随笔
今天看到一篇关于C++11的一些新的标准特性文章,虽然还没有环境,但是先随手记下。
1. auto关键字
这个关键字所声明的变量可以被赋值多种类型,应该说近乎所有类型,包括了STL容器的iterator类型等。
有点类似与Lua和Python的变量,但是不同的是,脚本的变量是不需要手动声明的。
例子:
auto i = 42; // i is an int
auto l = 42LL; // l is an long long
auto p = new foo(); // p is a foo*
std::map<std::string, std::vector<int>> map;
for(auto it = begin(map); it != end(map); ++it)
{
}
注意:auto能用于函数返回值声明的情况只存在于,当函数存在返回值类型时。也就是说,必须能够让编译在函数末端判断出返回值类型的情况下。
例子:
template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2)
{
return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double
2.关键字nullptr
用于取代NULL或者0,以防止前面两者隐式转换成整形。但是nullptr可以转化成bool,前两者也依旧合法。(向厚兼容)
3.for语句的拓展
可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。
std::map<std::string, std::vector<int>> map;
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
map["one"] = v;
for(const auto& kvp : map)
{
std::cout << kvp.first << std::endl;
for(auto v : kvp.second)
{
std::cout << v << std::endl;
}
}
int arr[] = {1,2,3,4,5};
for(int& e : arr)
{
e = e*e;
}
4. override 和 final 关键字
这两个关键字是为了虚函数而存在的。作者在这里举例子说明假如类中进行虚函数重写所遇到的麻烦,个人觉得不是麻烦就不记录了。
override关键字标记重写虚函数。
final关键字有点类似与java中用法,最终无法被重载。java中可以修饰class,不知道c++可以不,没有环境无法尝试,表示遗憾啊。。。
例子:
class B
{ public: virtual void f(int) { std::cout << "B::f" << std::endl; } }; class D : public B { public: virtual void f(int) override final {std::cout << "D::f" << std::endl;} }; class F : public D { public: virtual void f(int) override {std::cout << "F::f" << std::endl;} };
5.Strongly-typed enums强类型枚举
在枚举声明定义时添加关键字class。枚举常量不会再被暴露到外层作用域中,也不会隐式转化成整形。并且拥有用户指定的特定类型(传统枚举也增加了这个性质)。
例子:
1 enum class Options {None, One, All}; 2 Options o = Options::All;
接下来的几个不是很了解先记着:
6. SmartPointers智能指针
- unique_ptr: 如果内存资源的所有权不需要共享,就应当使用这个(它没有拷贝构造函数),但是它可以转让给另一个unique_ptr(存在move构造函数)。
- shared_ptr: 如果内存资源需要共享,那么使用这个(所以叫这个名字)。
- weak_ptr: 持有被shared_ptr所管理对象的引用,但是不会改变引用计数值。它被用来打破依赖循环(想象在一个tree结构中,父节点通过一个共享所有权的引用(chared_ptr)引用子节点,同时子节点又必须持有父节点的引用。如果这第二个引用也共享所有权,就会导致一个循环,最终两个节点内存都无法释放)。
1 void foo(int* p) 2 { 3 std::cout << *p << std::endl; 4 } 5 std::unique_ptr<int> p1(new int(42)); 6 std::unique_ptr<int> p2 = std::move(p1); // transfer ownership 7 8 if(p1) 9 foo(p1.get()); 10 11 (*p2)++; 12 13 if(p2) 14 foo(p2.get());
void foo(int* p) { } void bar(std::shared_ptr<int> p) { ++(*p); } std::shared_ptr<int> p1(new int(42)); std::shared_ptr<int> p2 = p1; bar(p1); foo(p2.get());
1 auto p3 = std::make_shared<int>(42);
make_shared<T>是一个非成员函数,使用它的好处是可以一次性分配共享对象和智能指针自身的内存。而显示地使用shared_ptr构造函数来构造则至少需要两次内存分配。除了会产生额外的开销,还可能会导致内存泄漏。在下面这个例子中,如果seed()抛出一个错误就会产生内存泄漏。
1 auto p = std::make_shared<int>(42); 2 std::weak_ptr<int> wp = p; 3 { 4 auto sp = wp.lock(); 5 std::cout << *sp << std::endl; 6 } 7 p.reset(); 8 if(wp.expired()) 9 std::cout << "expired" << std::endl;
lock()来获得被引用对象的shared_ptr,如果你试图锁定(lock)一个过期(指被弱引用对象已经被释放)的weak_ptr,那你将获得一个空的shared_ptr.
7. 非成员 begin()和end()
1 template <typename Iterator> 2 void bar(Iterator begin, Iterator end) 3 { 4 std::for_each(begin, end, [](int n) {std::cout << n << std::endl;}); 5 6 auto is_odd = [](int n) {return n%2==1;}; 7 auto pos = std::find_if(begin, end, is_odd); 8 if(pos != end) 9 std::cout << *pos << std::endl; 10 } 11 12 template <typename C> 13 void foo(C c) 14 { 15 bar(std::begin(c), std::end(c)); 16 } 17 18 template <typename T, size_t N> 19 void foo(T(&arr)[N]) 20 { 21 bar(std::begin(arr), std::end(arr)); 22 } 23 24 int arr[] = {1,2,3}; 25 foo(arr); 26 27 std::vector<int> v; 28 v.push_back(1); 29 v.push_back(2); 30 v.push_back(3); 31 foo(v);
8.static_assert和type traits
static_assert是一个编译时断言检查。
用法:
1 static_assert(Size < 3, "Size is too small");
接下来是:
9Lambdas和Move语义,这两个都不太理解也没用过,留着慢慢研究下。。。。。
按照个人理解,move最简单的理解就是,直接将临时变量的数组指针拿来用,使得临时变量不需释放指针,而所构造的变量不需要重新分配用来存储数组的内存,更少了copy的过程。
move直接从字面意思就可以很契合的解释了,类似于财产转移,将自己名下的数组转移给构造的变量。
而一个变量是否可以用于财产(数组)转移,又需要标志,所以有右值引用(&&,左值引用为单&)的概念。
这篇随笔的例子都是copy过来,就做简单记录吧,说不定要用到,可以翻看下。
浙公网安备 33010602011771号