快捷搜索:  汽车  科技

c语言中各种代码所表达的意义(开发者都应该使用的十个C)

c语言中各种代码所表达的意义(开发者都应该使用的十个C)Range-based for loops(基于范围的for循环)为了向前兼容,0仍然是个合法的空指针值。1234std::map<std::string std::vector<int>> map;for(auto it = begin(map); it != end(map); it){}需要注意的是,auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数的返回值类型就是operator 操作符作用在T1、T2类型变量上的返回值类型。nullptr以前都是用0来表示空指针的,但由于0可以被隐式类型转换为整形,这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值,

c语言中各种代码所表达的意义(开发者都应该使用的十个C)(1)

在C 11新标准中,语言本身和标准库都增加了很多新内容,本文只涉及了一些皮毛。不过我相信这些新特性当中有一些,应该成为所有C 开发者的常规装备。你也许看到过许多类似介绍各种C 11特性的文章。下面是我总结的,C 开发者都需要学习和使用的C 11新特性。

auto

在C 11之前,auto关键字用来指定存储期。在新标准中,它的功能变为类型推断。auto现在成了一个类型的占位符,通知编译器去根据初始化代码推断所声明变量的真实类型。各种作用域内声明变量都可以用到它。例如,名空间中,程序块中,或是for循环的初始化语句中。

123auto i = 42; // i is an intauto l = 42LL; // l is an long longauto p = newfoo(); // p is a foo*

使用auto通常意味着更短的代码(除非你所用类型是int,它会比auto少一个字母)。试想一下当你遍历STL容器时需要声明的那些迭代器(iterator)。现在不需要去声明那些typedef就可以得到简洁的代码了。

1234std::map<std::string std::vector<int>> map;for(auto it = begin(map); it != end(map); it){}

需要注意的是,auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。在下面这个例子中,函数的返回值类型就是operator 操作符作用在T1、T2类型变量上的返回值类型。

123456template<typenameT1 typenameT2>auto compose(T1 t1 T2 t2) -> decltype(t1 t2){returnt1 t2;}auto v = compose(2 3.14); // v's type is double

nullptr

以前都是用0来表示空指针的,但由于0可以被隐式类型转换为整形,这就会存在一些问题。关键字nullptr是std::nullptr_t类型的值,用来指代空指针。nullptr和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为bool型(取值为false)。但是不存在到整形的隐式类型转换。

123456789101112131415voidfoo(int* p) {}voidbar(std::shared_ptr<int> p) {}int* p1 = NULL;int* p2 = nullptr;if(p1 == p2){}foo(nullptr);bar(nullptr);boolf = nullptr;inti = nullptr; // error: A native nullptr can only be converted to bool or using reinterpret_cast to an integral type

为了向前兼容,0仍然是个合法的空指针值。

Range-based for loops(基于范围的for循环)

为了在遍历容器时支持”foreach”用法,C 11扩展了for语句的语法。用这个新的写法,可以遍历C类型的数组、初始化列表以及任何重载了非成员的begin()和end()函数的类型。

如果你只是想对集合或数组的每个元素做一些操作,而不关心下标、迭代器位置或者元素个数,那么这种foreach的for循环将会非常有用。

12345678910111213141516171819202122std::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(constauto& kvp : map){std::cout << kvp.first << std::endl;for(auto v : kvp.second){std::cout << v << std::endl;}}intarr[] = {1 2 3 4 5};for(int& e : arr){e = e*e;}

Override和final

我总觉得 C 中虚函数的设计很差劲,因为时至今日仍然没有一个强制的机制来标识虚函数会在派生类里被改写。vitual关键字是可选的,这使得阅读代码变得很费劲。因为可能需要追溯到继承体系的源头才能确定某个方法是否是虚函数。为了增加可读性,我总是在派生类里也写上virtual关键字,并且也鼓励大家都这么做。即使这样,仍然会产生一些微妙的错误。看下面这个例子:

1234567891011classB{public:virtualvoidf(short) {std::cout << "B::f"<< std::endl;}};classD : publicB{public:virtualvoidf(int) {std::cout << "D::f"<< std::endl;}};

D::f 按理应当重写 B::f。然而二者的声明是不同的,一个参数是short,另一个是int。因此D::f(原文为B::f,可能是作者笔误——译者注)只是拥有同样名字的另一个函数(重载)而不是重写。当你通过B类型的指针调用f()可能会期望打印出D::f,但实际上则会打出 B::f 。

另一个很微妙的错误情况:参数相同,但是基类的函数是const的,派生类的函数却不是。

1234567891011classB{public:virtualvoidf(int) const{std::cout << "B::f "<< std::endl;}};classD : publicB{public:virtualvoidf(int) {std::cout << "D::f"<< std::endl;}};

同样,这两个函数是重载而不是重写,所以你通过B类型指针调用f()将打印B::f,而不是D::f。

幸运的是,现在有一种方式能描述你的意图。新标准加入了两个新的标识符(不是关键字)::

1. override,表示函数应当重写基类中的虚函数。

2. final,表示派生类不应当重写这个虚函数。

第一个的例子如下:

1234567891011classB{public:virtualvoidf(short) {std::cout << "B::f"<< std::endl;}};classD : publicB{public:virtualvoidf(int) override {std::cout << "D::f"<< std::endl;}};

现在这将触发一个编译错误(后面那个例子,如果也写上override标识,会得到相同的错误提示):

1'D::f' : method with override specifier 'override' did not override any base class methods

另一方面,如果你希望函数不要再被派生类进一步重写,你可以把它标识为final。可以在基类或任何派生类中使用final。在派生类中,可以同时使用override和final标识。

1234567891011121314151617classB{public:virtualvoidf(int) {std::cout << "B::f"<< std::endl;}};classD : publicB{public:virtualvoidf(int) override final {std::cout << "D::f"<< std::endl;}};classF : publicD{public:virtualvoidf(int) override {std::cout << "F::f"<< std::endl;}};

被标记成final的函数将不能再被F::f重写。

感谢作者冯上(@治不好你我就不是兽医 ),本文转自伯乐在线

大家如果对编程感兴趣,想了解更多的编程知识,解决编程问题,我们这里有java高手,C /C高手,windows/Linux高手等,大家关注我的程序员互动联盟or coder_online

猜您喜欢: