笔者需要参与一项slam项目的开发,经年不用C++知识已经统统上交给了国家,尤其是现在用python炼丹调参不亦乐乎,怕是一时都改不过来习惯。
故决定速刷C++primer(六)一书,把忘掉的东西捡起来,希望不要和python搞混。
(只是熟悉语法的话看菜鸟教程查表更快,但是为了摸鱼汇报进度还是过一下书吧)
复合类型
数组
先举个例子int array1[12] = {3,4,5}
,声明数组类型和容量,数据不足默认补0,索引即用array[x]
。
字符串
字符串必定以“\0”结尾,为了避免初始化的麻烦,通常通过char array2[] = "I love python"
直接写入字符串,系统会自动补\0
。
- 这也导致c++里字符串常量和字符常量是一对容易混的概念。
"S"
和'S'
是两个不同的概念,"S"
是字符串常量,实际上是S
和\0
的数组,而'S'
则是字符常量,在ASCII标准里存在内存中的就是83。 - sizeof和strlen:sizeof()函数统计数组长度,而strlen统计字符串(不计空字符)长度,也可以调用方法,
字符串.size()
返回大小值。 - strcpy(目标,源)复制函数,strcat(目标,源)拼接函数。strcmp(比较1,比较2)比较字符串是否相同,接收字符串地址。
- 字符串输入:
cin.getline(数组名称,数组长度)
,不保留换行符。 - 字符串输入:
cin.get(数组名称,数组长度)
,保留换行符,所以连续输入几行会出现问题,下一次调用get会把enter当作输入,所以需要cin.get(xx,xxx).get()
来把enter跳过。但这是一个很好的检查验证的方式,比getline严谨。(未来会再探讨相关问题) - 当混合输入数字和字符串时就会遇到这种问题,输入数字后换行符还留在输入队列里,接下来调用cin.getline()就会读取换行符并识别为空。所以我们最好是把前面的输入都放在get方法里,如
(cin >> 某变量).get()
将换行符跳过读取新行。
String类
一个示例代码可以说明大多特性
#include <iostream>
#include <string> // make string class available
int main()
{
using namespace std;
string s1 = "penguin";
string s2, s3;
cout << "You can assign one string object to another: s2 = s1\n";
s2 = s1;
cout << "s1: " << s1 << ", s2: " << s2 << endl;
cout << "You can assign a C-style string to a string object.\n";
cout << "s2 = \"buzzard\"\n";
s2 = "buzzard";
cout << "s2: " << s2 << endl;
cout << "You can concatenate strings: s3 = s1 + s2\n";
s3 = s1 + s2;
cout << "s3: " << s3 << endl;
cout << "You can append strings.\n";
s1 += s2;
cout <<"s1 += s2 yields s1 = " << s1 << endl;
s2 += " for a day";
cout <<"s2 += \" for a day\" yields s2 = " << s2 << endl;
return 0;
}
- 输入:
getline(cin,字符串);
,这与读入数组的方法有所差异。 - 为了在字符串里可以输入很多需要转义的字符,可以使用
R"( 字符串 )"
,如果还想在字符串里显示)"
等元素,可以自定义标识头R"自定义字符( )自定义字符"
结构
struct stru1
{
string Company;
float diameter;
double Kcal;
};
stru1 name1 {"skdms s.ltd",24.5,886.9};
stru1 *pointer1 = new stru1;
stru1 *pointer2 = new stru1[5];
cin>> pointer1->diameter;
delete ...
这就是典型的结构声明与实例化。每一个结构体都有自己的结构名和内部定义的几个成员(属性),实例化结构为变量,可以初始化结构的成员值。也可以不实例化变量名,用指针初始化未命名的结构实例,以及结构数组。
指针
指针即地址,任何一个数据对象都有自己的数据类型和地址,因此也可以用地址(指针)来指代寻找对应的数据。
先给出两个关键符号,*
作为解除指针的标识符,&
作为数据的寻址符。
举个例子。
这里num1
是变量,string
是指针(地址):
int num1 = 12;
int* string;
string = &num1;
- 指针需要声明指向值的类型,如
int* AA,double* BB
等,且每个指针都需要声明*
。 - 指针初始化可以一步到胃。此时初始化的是指针而不是
*指针
int num1 = 12;
int* string = &num1;
- 指针必须初始化再去指代数据对象,否则数据可能无法寻址导致出错。
- …
指针的真正用武之地在于,在运行阶段分配未命名的内存来存储值
new&delete
举个例子:int* pointer = new int;delete pointer
,这样就分配了一个int
需要的内存空间,内存地址即pointer
,之后可以根据需要给*pointer
赋值,当不再需要时可以通过delete
释放内存,但是不会删除指针。
- 通常变量会在栈中开辟内存空间,而
new
则是在heap
里开辟空间。 delete
需要与new
成对使用,new
占用内存,delete
释放内存,但是指针不会被删除。- delete不能对同一内存块释放两次,但可以针对空指针。
- 创建动态数组时指针指向第一个元素,删除时应带
[]
。 - 索引动态数组元素可以直接把指针当作数组名进行索引:
int* ptr2 = new int[8];
ptr2[0] = 0.1;ptr[1] = 0.2;ptr[2] = 0.3;
ptr2 +=1;//将内存地址移到下一元素
cout<<ptr[0];//此时指向了原来的ptr[1]
delete [] pointer;
我们可以用这一组合创建动态数组、动态结构、结构数组等
struct stru1{int a;char b;float c;};
char *pointer1 = new char[8];//创建动态数组
stru1 *pointer2 = new stru1;//创建动态结构
stru1 *pointer3 = new stru1[5];//创建结构数组
cout<< pointer->a << stru1[2].b;//动态结构调用需要->符号
delete...
类型混合
#include <iostream>
using namespace std;
struct antarctica_years_end{int year;};
int main()
{
antarctica_years_end s01, s02, s03;
s01.year = 1998;
antarctica_years_end * pa = &s02;
pa->year = 1999;
antarctica_years_end trio[3]; // array of 3 structures
trio[0].year = 2003;
std::cout << trio->year << std::endl;
const antarctica_years_end * arp[3] = {&s01, &s02, &s03};
cout << arp[1]->year << std::endl;
const antarctica_years_end ** ppa = arp;
auto ppb = arp; // C++0x automatic type deduction
// or else use const antarctica_years_end ** ppb = arp;
cout << (*ppa)->year << std::endl;
cout << (*(ppb+1))->year << std::endl;
return 0;
}
数组(vector和array)
vector是动态数组,内置new和delete。效率低下;array是静态数组,更安全。使用前需要引用头文件。
double array1[4] = {2,1,4,6};
vector<double> array2(4);//容量可以不初始化指定,动态的优势
array<double,4> array3 = {12,23,34,45};
都是基础啦。
相关练习均已上传github