突破编程_C++_C++11新特性(nullptr、constexpr与基于范围的 for 循环)

本文介绍了C++11引入的nullptr关键字,提供了一种更安全的表示空指针的方法,以及constexpr的使用,允许在编译时计算值,提升代码性能。此外,还讨论了范围for循环的简洁语法及其在遍历容器中的应用。
摘要由CSDN通过智能技术生成

1 nullptr

C++11 引入了一个名为 nullptr 的新关键字,用于替换 C++98/03 中的 NULL 宏。nullptr 是空指针常量的一种类型安全的表示方法,它解决了使用 NULL 时可能遇到的一些问题和歧义。

1.1 NULL 的问题

在 C++98/03 中,NULL 通常被定义为 (void*)0 或 0,这取决于具体的实现。使用 NULL 有两个潜在的问题:

(1)类型不安全: NULL 可以隐式转换为任何指针类型,这可能导致一些类型混淆的错误。

(2)重载函数的歧义: 如果函数重载了接受 int 和指针参数的版本,使用 NULL 可能会导致调用错误版本的函数。

1.2 nullptr 的优势及示例

nullptr 的优势如下:

(1)类型安全: nullptr 是一个指针字面量,它只能转换为指针类型,不能转换为整数类型。

(2)消除重载函数的歧义: 使用 nullptr 可以确保调用正确版本的函数,因为它只能表示指针类型的空值。

nullptr 的示例如下:

示例 1:替换 NULL

#include <iostream>  
  
int* getPointer() {  
    // ...  
    return nullptr; // 返回空指针  
}  
  
int main() 
{  
    int* ptr = getPointer();  
    if (nullptr == ptr) { // 检查指针是否为空  
        std::cout << "Pointer is null." << std::endl;  
    } else {  
        std::cout << "Pointer is not null." << std::endl;  
    }  
    return 0;  
}

示例 2:消除重载函数的歧义

#include <iostream>  
  
void foo(int x) {  
    std::cout << "Called foo(int)" << std::endl;  
}  
  
void foo(int* ptr) {  
    if (nullptr == ptr) {  
        std::cout << "Called foo(int*) with null pointer." << std::endl;  
    } else {  
        std::cout << "Called foo(int*) with non-null pointer." << std::endl;  
    }  
}  
  
int main() 
{  
    foo(0);         // 调用 foo(int)  
    foo(nullptr);   // 调用 foo(int*)  
    return 0;  
}

在这个例子中,如果使用 NULL 替换 nullptr,那么 foo(NULL) 的调用将变得不明确,因为编译器无法区分应该调用哪个版本的 foo 函数。但是使用 nullptr 可以消除这种歧义。

1.3 注意事项

使用 nullptr 的注意事项如下:

  • 确保在编写新的 C++11 代码时使用 nullptr 替代 NULL。
  • 当升级旧代码时,逐步替换 NULL 为 nullptr,确保代码的正确性和兼容性。
  • 了解编译器和库是否支持 C++11,以确保 nullptr 可以被正确识别和使用。
  • 通过使用 nullptr,可以提高代码的类型安全性,并消除由于使用 NULL 而可能引入的错误和歧义。

2 constexpr

C++11 引入了一个非常强大的关键字 constexpr,它允许在编译时计算表达式的值,并且这些值可以用于初始化常量表达式。这不仅可以提高代码的可读性和性能,还可以用于创建模板元编程中的常量值。

2.1 constexpr 的基本用法

constexpr可以用于变量、函数或类的构造函数。当用于变量时,它表示该变量的值是一个常量表达式,必须在编译时确定。当用于函数或构造函数时,它表示该函数或构造函数的返回值是一个常量表达式。

示例 1:constexpr变量

constexpr int a = 5; // a是一个编译时常量  
constexpr int b = a * 2; // b的值在编译时确定为10

示例 2:constexpr函数

constexpr int square(int x) {  
    return x * x;  
}  
  
int main() 
{  
    constexpr int c = square(3); // c的值在编译时确定为9  
    return 0;  
}

2.2 constexpr 的限制和扩展

在 C++11 中,constexpr 函数有一些限制:

  • 它们只能包含单个返回语句。
  • 它们只能调用其他 constexpr 函数。
  • 它们不能包含非 constexpr 的变量或函数调用。

注意:从 C++14 开始,这些限制得到了放宽,允许 constexpr 函数有更多的灵活性,包括循环、条件语句等。

示例 1:C++14 中的 constexpr 函数

constexpr int factorial(int n) {  
    int result = 1;  
    for (int i = 1; i <= n; ++i) {  
        result *= i;  
    }  
    return result;  
}  
  
int main() 
{  
    constexpr int d = factorial(5); // d 的值在编译时确定为 120  
    return 0;  
}

constexpr 也可以用于类的构造函数,允许在编译时创建类的常量对象。

示例 2:constexpr 构造函数

#include <iostream>  
class Point {
public:
	constexpr Point(int x = 0, int y = 0) : m_x(x), m_y(y) {}
	constexpr int getX() const { return m_x; }
	constexpr int getY() const { return m_y; }

private:
	int m_x, m_y;
};

int main()
{
	constexpr Point p(1, 2); // p 是一个编译时常量对象  
	constexpr int e = p.getX(); // e 的值在编译时确定为 1  
	return 0;
}

2.3 使用 constexpr 的优势

使用 constexpr 的优势如下:

  • 性能提升:由于 constexpr 的值在编译时确定,所以不需要在运行时进行计算,这可以提高代码的执行效率。
  • 类型安全:使用 constexpr 可以确保常量表达式在编译时就是正确的,从而避免运行时错误。
  • 模板元编程:constexpr 在模板元编程中特别有用,因为元编程需要在编译时计算值。

2.4 注意事项

使用 constexpr 的注意事项如下:

  • 并不是所有的编译器都完全支持 C++11 和 C++14 中的 constexpr 特性,特别是在处理复杂表达式和函数时。因此,在使用 constexpr 时,最好检查编译器文档以确保兼容性。
  • 过度使用 constexpr 可能会使代码难以阅读和维护。通常,只在确实需要编译时常量表达式的地方使用它。
  • 通过掌握 constexpr 的用法,可以写出更加高效、安全的 C++ 代码,并利用编译时计算的优势来提升性能。

3 范围的 for 循环

基于范围的for循环(Range-based for loop)是 C++11 引入的一个新特性,它使得遍历容器或数组变得更加简单和直观。这种循环方式可以自动处理迭代器,使得代码更加简洁易读。

3.1 基本语法

基于范围的for循环的基本语法如下:

for (declaration : expression) {  
    // 循环体  
}

其中:

  • declaration:定义循环变量的类型。
  • expression:返回一个序列,通常是一个容器或数组。

3.2 示例

下面是一些使用基于范围的for循环的示例:

示例 1:遍历数组

#include <iostream>  
  
int main() 
{  
    int arr[] = {1, 2, 3, 4, 5};  
    for (int num : arr) {  
        std::cout << num << " ";  
    }  
    std::cout << std::endl;  
    return 0;  
}

输出:

1 2 3 4 5

示例 2:遍历 STL 容器

#include <iostream>  
#include <vector>  
#include <string> 
  
int main() 
{  
    std::vector<std::string> vec = {"apple", "banana", "cherry"};  
    for (const std::string& fruit : vec) {  
        std::cout << fruit << " ";  
    }  
    std::cout << std::endl;  
    return 0;  
}

输出:

apple banana cherry

示例 3:遍历自定义容器

只要自定义容器支持 begin() 和 end() 成员函数,并且这些函数返回的迭代器支持解引用操作,那么就可以使用基于范围的 for 循环来遍历它:

#include <iostream>  

class IntegerSequence {
public:
	IntegerSequence(int start, int end) : m_start(start), m_end(end) {}

	class iterator {
	public:
		iterator(int value) : m_current(value) {}

		bool operator==(const iterator& other) const {
			return m_current == other.m_current;
		}

		bool operator!=(const iterator& other) const {
			return !(*this == other);
		}

		int operator*() const {
			return m_current;
		}

		iterator& operator++() {
			++m_current;
			return *this;
		}

	private:
		int m_current;
	};

	iterator begin() const {
		return iterator(m_start);
	}

	iterator end() const {
		return iterator(m_end + 1);
	}

private:
	int m_start;
	int m_end;
};

int main() 
{
	IntegerSequence seq(1, 5);

	// 使用基于范围的for循环遍历自定义类型  
	for (int num : seq) {
		std::cout << num << " ";
	}
	std::cout << std::endl;

	return 0;
}

输出:

1 2 3 4 5

在上面的代码中,IntegerSequence 类包含了一个迭代器 iterator,该迭代器可以递增并返回当前值。IntegerSequence 的 begin 和 end 成员函数返回迭代器,分别指向序列的开始和结束位置。

在 main 函数中,创建了一个 IntegerSequence 对象,并使用基于范围的 for 循环遍历它。循环体会自动使用 begin 和 end 返回的迭代器,并在每次迭代时递增迭代器,直到它等于 end 返回的迭代器为止。

3.3 注意事项

如下是基于范围的 for 循环的一些注意事项:

  • 基于范围的 for 循环只能用于支持迭代器的序列。
  • 在循环体内,无法直接修改序列的大小(例如,在遍历 std::vector 时,不能添加或删除元素)。如果需要修改序列,则需要使用传统的迭代器循环。
  • 对于引用类型的循环变量(如示例 2 中的 const std::string& fruit),实际上是在引用序列中的元素,而不是复制它们。这意味着对循环变量的修改会影响序列中的实际元素(如果没有使用 const 的话)。
  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值