【C++】auto关键字(c++11)

C++11 auto与nullptr详解

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 概念

C++11中,auto不再是一个存储类型指示符,而是一个自动推导变量的类型,如:

#include <iostream>
#include <typeinfo>
using namespace std;

int TestAuto()
{
	return 10;
}

int main()
{
	int a = 10;
	auto b = a;//由a是int,可以推导出b的类型是int
	auto c = 'a';//由‘a’推导出c的类型是char
	auto d = TestAuto();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;

    auto e;//这条语句编译不通过,使用auto定义变量时,必须对其进行初始化
	system("pause");
	return 0;
}
//typeid(b).name()是打印类型名称的函数

运行结果:

需要注意的是:

使用auto定义变量时,必须对其进行初始化,因为auto并非是一种类型的声明,而是一个类型声明时的“占位符”,编译器在编译期间会将auto替换为变量实际的类型。

  • auto使用规则

1.auto与指针和引用结合起来使用。用auto声明指针类型肘,用auto和auto*没有任何区別,但用auto声明引用类型吋必须加&

#include <iostream>
#include <typeinfo>
using namespace std;

int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	
	system("pause");
	return 0;
}

2.在同一行定义多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其它变量。

#include <iostream>
using namespace std;

void TestAuto()
{
	auto a = 1, b = 2;
	//auto c = 3, d = 4.0;//错误
	auto c = 3, d = 4;//正确
	cout << c << endl;
	cout << d << endl;
}

int main()
{
	TestAuto();
	
	system("pause");
	return 0;
}
  • auto不能自动推导的场景

1.auto不能作为函数的参数

void TestAuto(auto a)//此处代码不通过,auto不能作为形参类型,因为编译器无法对a的实际类型进行自动推导
{
    ;
}

2.auto不能用来直接声明数组

void TestAuto()
{
	int a[] = { 1, 2, 3 };
	auto b[3] = a;//auto类型不能出现在顶级数组类型中
}

3.为了避免与c++98中的auto发生混淆,c++11中只保留了auto作为类型推导的用法

4.auto最常见的就是会跟c++11中的新式for循环,还有lambda表达式进行配合使用

#include <iostream>
using namespace std;

int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto e : array)//依次取array里面的元素读给e
		cout << e << endl;
	
	system("pause");
	return 0;
}

运行结果:

5.auto不能定义类的非静态成员变量

6.实例化模板时不能使用auto作为模板参数

  • 基于范围的新式for循环(c++11)

在C++98中如果要遍历一个数组,可以按照以下方式进行:

void TestFor()
{
intarray[]={1,2,3,4,5};
for (int i = 8; i < sizeof(array) / sizeof(array[0]); ++i )
   array[i] *= 2;

for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p)
  cout << *p << endl;

}

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号”:”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

 

int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto e : array)//依次取array里面的元素读给e
		cout << e << " ";
	
	system("pause");
	return 0;
}

如果此时想要打印array数组元素的2倍时,就必须加引用&:

#include <iostream>
using namespace std;

int main()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for(auto& e : array)//遍历数组,给数组的每个对象*2;
        e = e * 2;

    for (auto& e : array)//遍历数组,依次取出数组里面的元素,赋给e
        cout << e << " ";
    system("pause");
    return 0;
}

分析:不加“&”的意思是取出array里面的每个对象,赋给e,e虽然被改变了,但数组里面的对象并没有变;加“&”表示e是数组里面 每个对象的别名,此时e一改变,数组里面的每个对象也会改变,因为e是每个对象的别名。

注意:与普通循环类似,可以用continue来结束本次循环, 也可以用break来跳出整个循环。

 

  • for的使用条件

1. for循环迭代的范围必须是确定的,对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的方法,begin和end就是for循环迭代的范围,以下代码就有问题:

void Test(int array[])
{
	for (auto& e : array)
		cout << e <<endl;
}

分析:这个代码编译不通过,此时的array不再是数组而是一个指针,for的范围不确定。

2.for循环要支持迭代器,还得支持++和==。

  • nullptr(c++11)

 

在C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:

void TestPtr(){

int* p1 = NULL;int* p2 = 0;

而NULL实际是一个宏, 在传统的C头文件(stddef.h)中,可以看到如下代码:

 

#ifndef NULL
#ifdef_ .cplusplus
#define NULL  0
#else
#define NULL ((void *)0)
#endif
#endif

我们发现NULL在c++中被定义为整形常量0,在c语言中被定义为无类型指针(void* )的常量。这种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:

#include <iostream>
using namespace std;


void f(int)
{
	cout << "f(int)" << endl;
}
void f(int*)
{
	cout << "f(int*)" << endl;
}
int main()
{
	f(0);//调f(int)
	f(NULL);//调f(int)
	f((int*)NULL);//调f(int*)
	system("pause");
	return 0;
}

运行结果:

这个程序的本意是:想通过f(NULL)调用fint*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。在C++98中,字面常量0既可以是一个整形数字, 也可以是无类型的指针(Void)常量, 但是编译器默认情况下将其看成是一个整形常量, 如果要将其按照指针方式来使用,必须对其进行强转(void *)0。

  • nullptr

为了考虑兼容性,C++11并没有消除常量0的二义性,而是给出了全新的nullptr,表示空值指针。C++11为什么不在NULL的基础上进行扩展,这是因为NULL以前就是一个宏, 而且不同的编译器厂商对于NULL的实现可能不太相同,而且直接扩展NULL,可能会影响以前旧的程序。因此:为了避免混淆,C++11提供了nullptr,即: nullptr代表- 个指针空值常量。nullptr是有类型的, 其类型为nullptr. _t,仅仅可以被隐式转化为指针类型,nullptr. _t被定义在头文件中:

typedef decltype(nullptr) nullptr_ t;

需要注意的是:

1.在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。

2.在C++11中,sizeof(nullptr) 与sizeof(void*)0)所占的字节数相同。

3.为了提高代码的健壮性,建议在后续表示指针空值时,最好使用nullptr。

这里还有常见的一类题:

请区分:NULL、0、'\0'、"\0"?

NULL:是被定义出来的一个宏,它不是关键字,值为0;

0:是整形的0,值为0;

'\0':是字符0,值为0;

"\0":是字符串;

计算:

#include <iostream>
using namespace std;
int main()
{
    char str[] = "\0";
    cout << strlen(str)<< endl;//0字节
    cout << sizeof(str) << endl;//2个字节
    system("pause");
    return 0;
}

其中strlen(str)大小为0,因为转义字符\+0,就是字符串中的结束标志"\0",只要遇到"\0",它就自动停下来了,所以strlen(str)大小为0;sizeof(str)大小为2个字节,转义字符\+0是一个字符,还有字符串结束标志的那个"\0",加起来就是2个字节。

再比如:

#include <iostream>
using namespace std;

int main()
{
    char str[] = "\\0";
    cout << strlen(str)<< endl;//2个字节
    cout << sizeof(str) << endl;//3个字节
    system("pause");
    return 0;
}

这个程序里面strlen(str)大小为2个字节,转义字符\+\转义为\,后面还有个数字0,总共是2个字节;sizeof(str)大小为3个字节。

<think>好的,用户想了解C++auto关键字的用法和示例代码。首先,我需要回忆auto关键字C++中的作用。记得从C++11开始,auto用于自动类型推导,让编译器根据初始化表达式推断变量的类型。这样可以简化代码,特别是在类型名很长或者复杂的时候,比如迭代器或者lambda表达式。 接下来,我应该分几个主要用途来讲解。第一,基本类型推导,比如声明变量时替代显式类型声明。例如auto i = 42;会自动推断为int。第二,用于容器迭代,比如在vector的循环中使用auto&来避免写冗长的迭代器类型。第三,与函数返回值结合,不过C++14之后才支持auto作为函数返回类型,需要提一下可能需要尾置返回类型或者C++14的特性。第四,lambda表达式,用auto来存储lambda对象,这样可以方便地定义匿名函数。第五,结构化绑定,C++17的新特性,可以用auto来解包tuple或者结构体成员,这个可能对用户有帮助。 然后要考虑用户可能的疑问,比如auto的使用限制。比如,auto不能用于函数参数(除非C++20的泛型lambda),也不能用于非静态成员变量,或者在无法推断类型的情况下会报错。需要提醒用户注意这些情况,避免误用。 示例代码部分,每个用途都应该有对应的代码片段。比如基本类型推导的简单例子,容器迭代的循环示例,lambda表达式的使用,以及结构化绑定的例子。代码要简洁明了,突出auto的作用,同时注释解释关键点。 还要注意引用站内的资料,用户提供的引用中有提到auto与lambda和范围for循环的结合,以及类型推导的例子。需要将这些引用自然地融入到回答中,比如在讲解相关部分时添加引用标记[^2][^3]。 另外,用户的问题可能还涉及auto的优缺点,比如提高代码可读性,减少错误,但也可能隐藏类型信息导致理解困难。需要平衡说明,让用户了解何时适合使用auto。 最后生成的相关问题要围绕auto关键字展开,可能包括与decltype的区别、C++版本的影响、性能影响等,这些都是用户可能进一步想知道的内容。 检查是否有遗漏的部分,比如是否覆盖了主要用法,是否引用了正确的资料,示例代码是否正确,语法是否符合要求(行内公式用$,独立公式用$$)。确保回答结构清晰,分点明确,符合用户的要求。</think>### C++auto关键字的用法详解 #### 1. **基本类型推导** `auto`关键字允许编译器根据初始化表达式自动推导变量类型。例如: ```cpp auto x = 42; // x的类型推导为int auto str = "hello"; // str的类型推导为const char* std::vector<int> vec = {1, 2, 3}; auto it = vec.begin(); // it的类型推导为std::vector<int>::iterator ``` 通过`auto`可简化代码,尤其在复杂类型(如迭代器)中效果显著[^2][^3]。 --- #### 2. **用于容器遍历** 结合范围`for`循环,可简化容器元素的遍历: ```cpp std::vector<int> nums = {1, 2, 3}; for (auto& num : nums) { // 使用引用避免拷贝 num *= 2; // 修改容器内的值 } ``` 若无需修改元素,可用`const auto&`或`auto`直接拷贝。 --- #### 3. **与函数返回值结合** 从C++14开始,`auto`可用于函数返回值推导(需确保所有返回路径类型一致): ```cpp auto add(int a, int b) -> int { // C++11需要尾置返回类型 return a + b; } // C++14起可直接推导 auto multiply(double a, double b) { return a * b; } ``` --- #### 4. **处理Lambda表达式** `auto`可存储Lambda表达式对象,便于实现匿名函数: ```cpp auto lambda = [](int a, int b) { return a + b; }; int result = lambda(3, 5); // result = 8 ``` 结合泛型Lambda(C++14),功能更灵活: ```cpp auto generic_lambda = [](auto x, auto y) { return x + y; }; ``` --- #### 5. **结构化绑定(C++17)** 解包元组或结构体成员时,`auto`可简化代码: ```cpp std::tuple<int, std::string, double> data{42, "test", 3.14}; auto [id, name, value] = data; // 解包为独立变量 ``` --- ### 注意事项与限制 1. **无法推导未初始化变量** ```cpp auto x; // 错误:缺少初始化表达式 ``` 2. **不能用于非静态成员变量** ```cpp class Example { auto value = 10; // 错误:非静态成员需显式类型 }; ``` 3. **与引用和常量修饰符结合** 需显式指定`const`或`&`: ```cpp const auto& ref = get_large_object(); // 避免拷贝且不可修改 ``` --- ### 示例代码汇总 ```cpp #include <vector> #include <tuple> int main() { // 基本类型推导 auto x = 10; auto str = "C++"; // 容器遍历 std::vector<int> vec{1, 2, 3}; for (auto& num : vec) { num += 1; } // Lambda表达式 auto sum = [](auto a, auto b) { return a + b; }; auto result = sum(3.5, 4); // result类型为double // 结构化绑定 std::tuple<int, std::string> tpl{100, "example"}; auto [num, name] = tpl; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值