C/C++面试:为什么要引入nullptr

1059 篇文章 285 订阅

空指针 nullptr

nullptr出现的目的是为了替代NULL

在某种意义上来说,传统 C++ 会把 NULL、 0 视为同⼀种东⻄,这取决于编译器如何定义 NULL,有些编译器会将NULL 定 义为((void*)0),有些则会直接将其定义为 0。C++ 不允许直接将void * 隐式转 换到其他类型,但如果NULL 被定义为((void*)0),那么当编译 char *ch = NULL; 时,NULL 只好被定义为 0。⽽这依然会产⽣问题,将导致了 C++ 中重载特性 会发⽣混乱,考虑:

void func(int);
void func(char *);

对于这两个函数来说,如果 NULL ⼜被定义为了 0 那么 func(NULL) 这个语句将 会去调⽤ func(int),从⽽导致代码违反直观。

为了解决这个问题,C++11 引⼊了 nullptr 关键字,专⻔⽤来区分空指针、0。nullptr 的类型为nullptr_t,能够隐式 的转换为任何指针或成员指针的类型,也能和他们进⾏相等或者不等的⽐较。

当需要使⽤ NULL 时候,养成直接使⽤ nullptr 的习惯。

右值引⽤

C++03以及之前的标准中,右值是不允许改变的,实践中也通常使用const T&的方式传递右值。然而这是效率低下的做法,比如:

Person get(){
 Person p;
 return p;
}
Person p = get();

上述获取右值并初始化 p 的过程包含了 Person 的3个构造过程和2个析构过程。 这是 C++ ⼴受诟病的⼀点,但是C++11的右值引用特性允许我们对右值进行修改。借此可以实现move语义,即从右值中直接拿数据过来初始化或者修改左值,而不需要重新构造左值后再析构右值。一个move构造函数是这样声明的:

class Person{
public:
 Person(Person&& rhs){...}
 ...
}

常量表达式

对于如下:

int N = 5;
int arr[N];

编译器会报错: error: variable length array declaration not allowed at file scope int arr[N]; ,但 N 就是5,不过编译器不知道这⼀点,于是我们需要声明为 const int N = 5 才可以。但C++11的泛化常数给出了解决⽅案:

constexpr int N = 5; // N 变成了⼀个只读的值
int arr[N]; // OK

constexpr 告诉编译器这是⼀个编译期常量,甚⾄可以把⼀个函数声明为编译期常量表达式。

constexpr int getFive(){ return 5; }
int arr[getFive() + 1];

final 和 override

C++借由虚函数实现运行时多态,但C++的虚函数又有很多脆弱的地方:

  • ⽆法禁⽌⼦类重写它。可能到某⼀层级时,我们不希望⼦类继续来重写当前虚函数了。
  • 容易不⼩⼼隐藏⽗类的虚函数。⽐如在重写时,不⼩⼼声明了⼀个签名不⼀致但有同样名称的新函数。

C++11提供了final来禁止虚函数被重写/禁止类被继承,override来显式的重写虚函数。这样编译器给我们不小心的行为提供更多有用的错误和警告

struct Base1 final { };
struct Derived1 : Base1 {}; // 编译错:Base1不允许被继承
struct Base2 {
	 virtual void f1() final;
	 virtual void f2();
};
struct Derived2 : Base2 {
	 virtual void f1(); // 编译错:f1不允许᯿写
	 virtual void f2(int) override; // 编译错:⽗类中没有 void f2(int)
};

default 和 delete

我们知道编译器会为类自动生成一些方法,比如构造和析构函数。

现在我们可以显示的指定和禁止这些自动行为了

struct classA {
	 classA() = defauult; // 声明⼀个⾃动⽣成的函数
	 classA(T value);
	 void *operator new(size_t) = delete; // 禁⽌⽣成new运算符
};

在上述 classA 中定义了 classA(T value) 构造函数,因此编译器不会默认⽣成⼀个⽆参数的构造函数了, 如果我们需要可以⼿动声明,或者直接 = default 。

静态断言

C++提供了两种方式来assert:

  • 一种是assert宏
  • 一种是预处理指令#error

前者在运行期起作用,后者在预处理器起作用。

它们对模板都不好使,因为模板是是编译器概念。为此引入了static_assert

template< class T >
struct Check {
 static_assert( sizeof(int) <= sizeof(T), "T is not big enough!" ) ;
} ;
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值