C/C++11的小语法与知识点
1.auto
-
在c/c++早期的时候,auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量
-
auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器。auto声明的变量必须由编译器在编译时期推导而得的
//必须在新的编译器才可以跑过,旧的会出错
#include <iostream>
using namespace std;
int TestAuto()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;\\b是int类型
auto c = 'a';\\c是char类型
auto d = TestAuto();\\d是int类型
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}
【注意】:
使用auto定义变量时必须对其进行初始化,在编译阶段编译器根据初始化表达式来推导auto的实际类型,因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”。编译器在编译期间会将auto替换为变量实际的类型。
1.1 auto与指针连用
auto与指针和引用结合起来使用
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
#include <iostream> using namespace std; int main() { int x = 10; auto a = &x;//int* auto* b = &x;//int* auto& c = x;//int,引用也是x,所以类型也是int cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; *a = 20; *b = 30; c = 40; return 0; }
在同一行定义多个变量
当在同一行定义多个变量时,这些变量必须是相同类型的,否则编译器会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
void TestAuto() { auto a = 1, b = 2; auto c = 3, d = 4.0;//改行代码会编译失败,因为c和d的初始化表达式类型不一样 }
auto不能推导的场景
1.auto不能作为函数的参数
//此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导 void TestAuto(auto a) { }
2.auto不能直接用来声明数组
void TestAuto() { int a[] = { 1, 2, 3 }; auto b[] = { 1, 2 }; }
3.auto不能定义类的非静态成员变量
4.实例化模板时不能使用auto作为模板参数
2. nullptr和nullptr_t
为了考虑兼容性,c++11并没有消除常量0的二义性,c++11给出了全新的nullptr表示空值指针,c++11为什么不在NULL的基础上进行扩展,这是因为NULL以前就是一个宏,而且不同的编译器商对于NULL的实现可能不太相同,而且直接扩展NULL,可能会影响以前旧的程序,因此,为了避免混淆,c++11提供了nullptr,即:nullptr代表一个指针空值常量。nullptr是有类型的,其类型是nullptr_t,仅仅可以被隐式转化为指针类型,nullptr_t被定义在头文件中:
typedef decltype(nullptr) nullptr_t;
【注意】:
在使用nullptr表示指针空值的时候,不需要包含头文件,因为nullptr是c++11作为关键字引入的
在c++11中,sizeof(nullptr) 和sizeof(void* 0)所占字节数相同
为了提高代码的健壮性,在后续表示空值指针是建议最好使用nullptr
3. 在linux下显式的使用c++11
-
对于一般的linux下的g++编译器都是支持c++98的,如果想让其支持c++11编译的话,那就只需要显式打开c++11就好了
#include <iostream> #include <typeinfo> using namespace std; int TestAuto() { return 10; } class Test { public: Test(int a = 0) : _a(a) {} private: int _a = 1;//c++11支持对于非静态成员变量直接进行初始化 }; int main() { int a = 10; auto b = a; auto c = 'a'; auto d = TestAuto(); Test e; auto f = e; cout << typeid(b).name() << endl;//int cout << typeid(c).name() << endl;//char cout << typeid(d).name() << endl;//int cout << typeid(e).name() << endl;//class Test cout << typeid(f).name() << endl;//class Test //auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化 return 0; }
对于上面的代码,具有c++11的新语法auto,如何对其进行编译呢?
g++ -o auto auto.cpp //采用这种编译的话,就显示 //‘b’ does not name a type //表示该编译器不支持c++11,所以就要显式的使用c++11编译 g++ -std=c++11 -o auto auto.cpp //这种进行编译的话就不会出错,显式的使用c++11
4. typeinfo 库
包含typeid().name()工具函数,用来返回变量的数据类型,学习这个函数
typeid操作符,其返回结果为名为type_info的标准库类型的对象的引用。type_info中存储特定类型的有关信息,定义在typeinfo头文件中
typeid().name(),用来获取表达式的类型,以c-style字符串形式返回引用名。
【注意】:对于非引用类型,typeid().name()在编译时期识别,只有引用类型才会在运行时识别。
例:
#include <iostream> #include <typeinfo> using namespace std; int TestAuto() { return 10; } class Test { public: Test(int a = 0) : _a(a) {} private: int _a = 1;//c++11支持对于非静态成员变量直接进行初始化 }; int main() { int a = 10; auto b = a; auto c = 'a'; auto d = TestAuto(); Test e; auto f = e; cout << typeid(b).name() << endl;//int cout << typeid(c).name() << endl;//char cout << typeid(d).name() << endl;//int cout << typeid(e).name() << endl;//class Test cout << typeid(f).name() << endl;//class Test //返回值是char const *,表示该函数的返回值是一个字符串(字符指针) cout << typeid(typeid(f).name()).name() << endl;//char const * //auto g; 无法通过编译,使用auto定义变量时必须对其进行初始化 return 0; }
【面试题】:
对于可以使用typeid().name()函数的返回值做为类型名进行定义吗?
不可以
由于typeid().name()函数返回的是一个字符串,肯定不能用于定义
5. 内置类型
即某个语言初期的时候定义的类型就叫做内置类型
6. delete函数(c++11)
//声明该函数,但是不需要进行实现
HeapType (const HeapType& ht) = delete;
7. 匿名对象
//生命周期只有一行
Date d1;//正常对象
Date();//这就是匿名对象
8. 定制化的为一个类重载operator new和operator delete
//如果你重载了operator new和operator delete
//那么该类使用new的时候就会使用你写的operaor new和operator delete
void* operator new(size_t sizt) = delete;
viod operator delete(void* p) = delete;
//没有禁用掉全局和静态的对象产生
//对于其他对象new时,不会影响其他对象的new
9. size_t
无符号整数类型
size_t是sizeof运算符返回的类型,广泛应用于标准库中以表示大小和计数
10. C++不加.h
对于C++加不加.h,其实都是可以的,C++的标准是不需要加.h的,比如C语言中的stdio.h和string.h,在C++中就是stdio和string或者cstdio和cstring。加了C之后表示该头文件是从C语言演变过来的,对于C的那是字符串函数,而对于C++那是string对象
11. getline的用法
//从流cin中读取数据,写到str中,直到遇到delim istream& getline(istream& cin, string& str, char delim); //从流中读取数据写到str,默认遇到空格或者换行终止 istream& getline(istream& cin, string& str);
【作用】
对于该全局函数,包含在string头文件中
从流中读取数据,是不会读取到分隔符,只会讲终止符前面的数据读取到str中
12. cin.getline的用法
//默认的分隔符为空格或者换行 istream& getline(char* s, streamsize n);//streamsize是有符号字符大小 //分隔符为delim istream& getline(char* s, streamsize n, char delim);
【作用】
对于该函数是属于cin的
直接从cin中读取字符,直到遇到分隔符停止,或者已经将n个字符写入到s中,实际是读入n-1个字符,最后一个是终止符。(包括终止定界符)
13. 为什么main函数会有返回值
平常看到的mian函数都会有着自己的类型,都是int类型
int main() { return 0; }
对于main函数为什么要有着自己的返回值
由于main函数也是一个进程运行起来的话,对于一个进程当其运行的时候,那么该进程肯定就会有着自己的父进程,那么当main函数结束的时候,父进程就要知道main函数的退出状态,所以就要有着返回值了。对于这个返回值一般都是0,返回0的话,默认该进程执行成功,正确返回。
所以main函数的返回值一般都是0(return 0)
如果不写return 0的话,那么对于c99之后的编译器都会自己默认返回0
14. 测试vector的增容情况
//测试vector自我开辟容量的 #include <iostream> #include <vector> int main() { std::vector<int> varray; size_t sz = varray.capacity(); std::cout << "test vector capacity!" << std::endl; for (size_t i = 0; i < 1000; i++) { varray.push_back(i); if (varray.capacity() != sz) { sz = varray.capacity(); std::cout << "capacity change:" << sz << '\n'; } } return 0; }
对于在g++下和VS2017下STL库中的vector容器增容的方法是不一样的:
对于g++下:(SGI版本的STL)
vector是按照2倍增容的
对于VS下:(PJ版本的STL)
vector是按照1.5增容的
15. 对于for+auto遍历容器
对于使用for+auto语法糖 遍历一个容器的时候需要什么东西呢?
首先肯定是需要迭代器的。
但是都需要什么迭代器呢?
今天突然写到这个的一个代码。就是对于vector容器的自我实现
由于vector容器实现的时候需要三个模板指针,_start, _finish, _endofstorage
这三个模板类型的指针,分别代表的意思是
_start:vector容器的起始地址
_finish:vector容器所包含大小的下一个地址也就是, _start+size();
_endofstorage:vector容器的最大容量的下一个地址, _start + capacity();
那么我写了这个的一个代码。写了一个赋值运算符重载
我写的时候,忘了对于_finish进行增加,导致最后的时候,_finish和 _satrt是相等的,都指向该vector容器的开头,
然后我再用三种不同的方法进行遍历:
for+auto(需要使用迭代器)
for+[](不需要使用迭代器)
for+iterator(需要使用迭代器)
这三种方法分别遍历:
for + iterator遍历和for+auto进行出错。对于for + [],这个肯定是正确的
说明对于for+iterator和for+auto都是需要begin()迭代器和end()迭代器
16. 在一个类型后面直接加上()表示啥意思
对于这个()表示的的意思就是创建了一个对象。并且将该对象初始化为0.