C++11学习笔记(五)

【指针空值——nullptr】

#include <iostream>using namespace std;void f(char* c) {cout << "invoke f(char*)" << endl;}void f(int i) {cout << "invoke f(int)" << endl;}int main() {f(0);// f(NULL); // 注意:如用gcc编译,NULL转化为内部标示__null,该句会编译失败。f((char*)0);}本程序中,NULL被定义为0,这里引发错误的原因是 C++98中,0既可以是一个整形,也可以是一个(void*)指针。如果想要调用f(char* c)版本,就必须进行强制转换。 f((char*)0);

C++11标准中,因为要保持兼容性,所以并没有消除上面情形的二义性,而是引入了关键字nullptr——其实质是一个nullptr_t类型的常量,定义如下

typedef decltype(nullptr) nullptr_t;使用nullptr_t时,必须#include<cstddef>,而nullptr则不用。

正是由于nullptr是有类型的,使用它来替代NULL可以使得代码更加健壮。

#include <iostream>using namespace std;void f(char *p) {cout << "invoke f(char*)" << endl;}void f(int) {cout << "invoke f(int)" << endl;}int main(){f(nullptr); // 调用f(char*)版本f(0);// 调用f(int)版本return 0;}代码准确的表达了程序员的意图。

下面来看一些nullptr_t的一些规则

所有nullptr_t类型的数据都是等价的,行为也是完全一致;

nullptr_t类型可以隐式转换成任意一个指针类型、不适用于算术运算表达式等。

下面的代码集合了大多数场景

#include <iostream>#include <typeinfo>using namespace std;int main(){// nullptr可以隐式转换为 char*char * cp = nullptr;// 不可转换为整型,而任何类型也不能转换为nullptr_t,// 以下代码不能通过编译// int n1 = nullptr;// int n2 = reinterpret_cast<int>(nullptr);// nullptr与nullptr_t类型变量可以作比较,// 当使用"==", "<=", ">="符号比较时返回truenullptr_t nptr;if (nptr == nullptr)cout << "nullptr_t nptr == nullptr" << endl;elsecout << "nullptr_t nptr != nullptr" << endl;if (nptr < nullptr)cout << "nullptr_t nptr < nullptr" << endl;elsecout << "nullptr_t nptr !< nullptr" << endl;// 不能转换为整型或bool类型, 以下代码不能通过编译// if (0 == nullptr);// if (nullptr);// 不可进行算术运算, 以下代码不能通过编译// nullptr += 1;// nullprt * 5;// 以下操作均可以正常进行sizeof(nullptr);typeid(nullptr);throw(nullptr);return 0;}运行结果如下

最后说说 nullptr_t在模板中的应用。

#include <iostream>using namespace std;template<typename T> void g(T* t) {}template<typename T> void h(T t) {}int main(){ g(nullptr);// 编译失败, nullptr的类型是nullptr_t,而不是指针 g((float*) nullptr); // 推导出T = floath(0);// 推导出T = int h(nullptr);// 推导出T = nullptr_t h((float*)nullptr); // 推导出T = float*}虽然nullptr_t是一个指针类型,但在模板中,仍把它当作一个普通类型来推导。

【默认函数的控制】

在C++中声明自己的类,编译器会为我们生成一些自定义的函数,这些函数被称为默认函数。比如默认的构造函数、复制构造函数、析构函数等。

一旦我们写了这些函数自己的版本,则编译器不再为我们生成默认版本,如此一来,将导致我们自定义的类不再是POD类型。

#include <type_traits>#include <iostream>using namespace std;class TwoCstor {public:// 提供了带参数版本的构造函数,则必须自行提供// 不带参数版本,且本class不再是POD类型TwoCstor() {};TwoCstor(int i): data(i) {}private:int data;};int main(){cout << is_pod<TwoCstor>::value << endl;}C++11中,重用了default这个关键字,可以通过它显式指示编译器生成它的默认版本。#include <type_traits>#include <iostream>using namespace std;class TwoCstor {public:// 提供了带参数版本的构造函数,再指示编译器// 提供默认版本,则本class依然是POD类型TwoCstor() = default;TwoCstor(int i): data(i) {}private:int data;};int main(){cout << is_pod<TwoCstor>::value << endl;}有时候,我们希望能够限制一些默认函数的生成。比如,需要禁止复制构造函数被调用。在C++98中,通常通过将其访问级别设为private。#include <type_traits>#include <iostream>using namespace std;class NoCopyCstor {public:NoCopyCstor() = default;private:// 将拷贝构造函数声明为private成员并不提供实现// 可以有效阻止用户错用拷贝构造函数NoCopyCstor(const NoCopyCstor &){};};int main(){NoCopyCstor a;NoCopyCstor b(a); // 无法通过编译}在C++11中,引入了更为简单的方法——在函数的定义或声明中加上“=delete”。#include <type_traits>#include <iostream>using namespace std;class NoCopyCstor {public:NoCopyCstor() = default;// 使用 "= delete" 同样可以有效阻止用户// 错用拷贝构造函数NoCopyCstor(const NoCopyCstor &) = delete;};int main(){NoCopyCstor a;NoCopyCstor b(a); // 无法通过编译}注意,一旦缺省版本被删除,即便是重载该函数也是非法的。

只要有信心,人永远不会挫败

C++11学习笔记(五)

相关文章:

你感兴趣的文章:

标签云: