C++11学习笔记(五)
2015-11-30 17:40
453 查看
【指针空值——nullptr】
C++11标准中,因为要保持兼容性,所以并没有消除上面情形的二义性,而是引入了关键字nullptr——其实质是一个nullptr_t类型的常量,定义如下
使用nullptr_t时,必须#include<cstddef>,而nullptr则不用。
正是由于nullptr是有类型的,使用它来替代NULL可以使得代码更加健壮。
代码准确的表达了程序员的意图。
下面来看一些nullptr_t的一些规则
所有nullptr_t类型的数据都是等价的,行为也是完全一致;
nullptr_t类型可以隐式转换成任意一个指针类型、不适用于算术运算表达式等。
下面的代码集合了大多数场景
运行结果如下
![](https://img-blog.csdn.net/20151130164213680?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
最后说说 nullptr_t在模板中的应用。
虽然nullptr_t是一个指针类型,但在模板中,仍把它当作一个普通类型来推导。
【默认函数的控制】
在C++中声明自己的类,编译器会为我们生成一些自定义的函数,这些函数被称为默认函数。比如默认的构造函数、复制构造函数、析构函数等。
一旦我们写了这些函数自己的版本,则编译器不再为我们生成默认版本,如此一来,将导致我们自定义的类不再是POD类型。
C++11中,重用了default这个关键字,可以通过它显式指示编译器生成它的默认版本。
有时候,我们希望能够限制一些默认函数的生成。比如,需要禁止复制构造函数被调用。在C++98中,通常通过将其访问级别设为private。
在C++11中,引入了更为简单的方法——在函数的定义或声明中加上“=delete”。
注意,一旦缺省版本被删除,即便是重载该函数也是非法的。
“=default”不仅能被用在成员函数上,在类的外部也可以使用
显式删除 “=delete”可以避免编译器做一些不必要的隐式数据类型转换。
#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类型变量可以作比较, // 当使用"==", "<=", ">="符号比较时返回true nullptr_t nptr; if (nptr == nullptr) cout << "nullptr_t nptr == nullptr" << endl; else cout << "nullptr_t nptr != nullptr" << endl; if (nptr < nullptr) cout << "nullptr_t nptr < nullptr" << endl; else cout << "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 = float h(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); // 无法通过编译 }
注意,一旦缺省版本被删除,即便是重载该函数也是非法的。
“=default”不仅能被用在成员函数上,在类的外部也可以使用
class DefaultedOptr{ public: // 使用"= default"来产生缺省版本 DefaultedOptr() = default; // 这里没使用"= default" DefaultedOptr & operator = (const DefaultedOptr & ); }; // 在类定义外用"= default"来指明使用缺省版本 inline DefaultedOptr & DefaultedOptr::operator =( const DefaultedOptr & ) = default;
显式删除 “=delete”可以避免编译器做一些不必要的隐式数据类型转换。
class ConvType { public: ConvType(int i) {}; ConvType(char c) = delete; // 删除char版本 }; void Func(ConvType ct) {} int main() { Func(3); Func('a'); // 无法通过编译 ConvType ci(3); ConvType cc('a'); // 无法通过编译 }
相关文章推荐
- 在 Qt4 中使用 C++11
- centos安装devtoolset-3支持gcc 4.9.2
- 使用eclipse编译含有C++11特性的代码
- 怎样在Linux环境编译支持C11
- eclipse支持c++11
- C++11可变参数函数与for循环
- vs2013 编译c++是发现惊天bug
- 简单性能测试函数模板
- 关于C++现状的一些思考
- 用C++11优化矩阵运算的空间和时间效率
- 浅析构造函数之默认构造函数
- c++中返回数组的函数
- C++函数重载的几个问题
- C++11 note-2 字符串 容器 迭代器
- C++11 现代C++风格的新元素
- c++11 lambda表达式浅谈
- C++11之“move”语意
- C++11之Lambda表达式
- C++11特性乱弹
- 掀起C++ 11的神秘面纱(1)