您的位置:首页 > 编程语言 > C语言/C++

C++11学习笔记(五)

2015-11-30 17:40 453 查看
【指针空值——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类型变量可以作比较,
// 当使用"==", "<=", ">="符号比较时返回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');   // 无法通过编译
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++11