c++博大精深, 越看越觉得不会的太多. 回炉一下吧~ 大神们 请勿浪费时间~
跟c#类似 如std::cin 使用using , using std::cin 后 程序中直接使用cin即可.
每个声明只能引入空间中的一个成员, 不能像C# 那样使用*
头文件中不应用using
无论是直接=赋值还是使用 string s("123123") string s2(s) 都是赋值, 而不是引用, 新变量都是保存的副本. string s3 (n, 'c') 使用n个c初始化s3, = 拷贝初始化, 而使用() 是直接初始化.
string的一些操作
s.emtpy() 为啥不搞个s.isEmtpy() 多明显呢!!
需要注意的是s.size() 返回的是一个string::size_type 类型, 本质是一个无符号的类型的值, 为了机器无关性, 设计的一个类型. 尤其需要注意的是, 如果是 s.size() < n, n如果是负值, 则刚才的判断几乎肯定是true, 因为n在参与无符号运算时会自动转换为无符号值, 此时n会很大!
字符大小比较: 如果大小写都相同长度不同, 长的>短的
如果字符都不相同, 则比较的是第一对相同位置上的字符大小
如 string str1="hello", str2="hi"; 则 str2>str1
+, += 字符串连接, 必须确保符号两边至少有一个对象是string
string str= "123" + "aaa"; //此处+ 不是字符串连接
请注意一下两者的区别:
string str1 = "123"
string str2 = str1 + "111" + " 222 "; // √ 第一个+ 会产生一个string, 然后 和第二个+运算 都符合要求
string str3 = " 111 " + "222" + str1; // × 第一个不能产生string, 所以错误
这些函数为啥不能使用isSpace(c) 的形式呢, 看着都费劲!
string str("hello world c++");
for (auto a : str) std::cout << " a in str :" << a; // 遍历 读取
for (auto& a : str) { // 遍历 修改
a = std::toupper(a);
}
for (auto a : str) {
auto* b = &a; // 通过指针的方式 修改str中的值, 比较二的但能实现目的
*b = std::toupper(a);
}
或者使用下标 修改. str[x] = xx; 其下标类型为string::size_type 或者能转化为该类型的类型
#include
using std::vector;
vector 不能包含引用, 因为引用不是对象
如果提供的是初始元素值的列表, 只能把初始值放在{}中
vector
vector
vector
vector
vector
如果提供的值不能跟要求的类型相符, 则会尝试初始化n个默认值, 如
vector
如果循环体内包含添加动作, 则不能用范围for循环?? 会引起for循环判断失效???
注意函数size 的返回值类型是 vectorvector::size_type 则是错误的定义
如果内部元素不能比较大小, 则vector也不能比较大小
迭代器跟指针很像, 但是跟指针又有很多不同, 迭代器的运算是指自己向前/后移动, 指针则是地址.
迭代器中的end, 并不是指向最后一个元素,而是指向最后一个元素的下一个位置, 其不能++或--
*iter 返回迭代器iter所指元素的引用
iter->xx 解引用iter并获取元素的名为xx的成员
如果对象为空, 则其begin和end迭代器相同
为何C++的for循环中不常用<, <=, >, >= 判断结束, 是因为c++标准库中容器的迭代器 都实现了!=和==, 所以不能用上面的判断,而是用!=判断 怪不得看的难受
vector
begin end cbegin cend 后面两个只返回const_iterator
凡是使用了迭代器的循环体, 都不要试图修改迭代器所属容器的大小.
数组的大小固定, 灵活性不如vector, 性能优于vector
数组的维度必须是常量. int *parr[10]; 含有10个整型指针的数组,而不是指向含有10个元素的数组的指针
int (*p)[10] = &arr; p 指针, 指向一个10个元素的数组
int (&arrRef)[10] = arr; arrRef 引用, arr的别名
注意使用字符串字面值初始化字符数组时的'\0' 问题, 该空字符会占用一个位置
不能使用数组初始化另一个数组
不能把一个数组直接赋值给另一个数组
数组的下标类型是size_t 机器相关的无符号类型.
使用数组的名字时, 编译器会自动讲数组名替换为指向数组第一个元素的指针,特性
如string *p2 =nums, 等价于string *p2 = &nums[0]
auto 推断数组时 得到是指针, 而decltype 则会推断出真正的类型
数组的指针 也是 迭代器
尾后指针 = &arr[数组长度] 或者使用end函数 (begin用于获取首指针)
尾后指针 因为指向的是一个不存在的对象, 所以不能解引用和递增
数组的指针也可以进行下标运算, 如
int arr[2]={1, 2}
int *p=arr, 而 p[1] 等价于*(p+1) 等于arr[1], 还可以是负数如 p[-2]
慎用 负数, 如果负数超出范围 编译器 不报错, 但是其指向的数据 不知道是什么
c中的几个函数
strlen 返回p的长度, 空字符不计算在内, 如果传入的是指针, 指针指向的对象如果没有'\0' 则会发生严重错误
strcmp(p1, p2) 比较p1和p2的相等性, p1==p2 return 0, p1>p2 return 正, 反之 返回负
strcat(p1, p2) p2 附加到p1, 返回p1
strcpy(p1,p2) 将p2 拷贝给p1 , 返回p1
尽量使用标准库string, 不要使用c风格字符串, c风格字符串不安全且低效.
为了兼容 允许使用以空字符结束的字符数组初始化string或赋值, string 加法运算中也可以出现一个以空字符结束的字符数组.
string 转 cha* 需要使用string的c_str(), 结果为const char*
字符数组可以初始化vectory 如 vector
int ia[3][4] = {{0,1,2,3}...} = {0,1,2,3,...} 内层{}不是必须的, 但是还是带上清晰明了.
如果只初始化每行首元素
int ia[3][4] = {{0],{4}, {8}} {}不能省略.
注意这种引用定义
int (&row)[4] = ia[1], row绑定到ia的第二行上.
在多维数组使用范围for遍历时需要使用引用, 否则 会被自动转化为指针.
如何理解int * p[4] 和int (*p)[4]
int * p[4] 可以参照 vs2019中的默认格式,把int* 作为一个整体即 (int*) p[4] ,理解时等同于int p[4], 就可以理解为一个数组,里面存放了4个整型指针.
int (*p)[4] = int a [4], a=*p p 是一个指针, 该指针指向一个4个整数的数组.
可以使用using 或者typedef 定义别名, 个人不建议, 感觉更乱.
如 using int_arry = int[4] 或者 typedef int int_array[4]
一个练习题: 输出数组ia的元素, 方法1 使用for语句管理迭代过程, 方法2 使用下标, 方法3 使用指针, 方法4 auto
int ia[3][4] = {
{0,1,2,3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};
//方法1
for (int (&row)[4] : ia) {
for (int &col : row) {
std::cout << col;
}
std::cout << std::endl;
}
//方法2
size_t row = 0;
size_t col = 0;
for (row = 0; row < 3; ++row) {
for(col=0; col<4 ; ++col) std::cout << ia[row][col];
std::cout << std::endl;
}
//方法3
for (int (*p)[4] = std::begin(ia); p != std::end(ia); ++p) {
for(int *p2 = *p; p2 != std::end(*p); ++p2) std::cout << *p2;
std::cout << std::endl;
}
//方法4
for (auto& row : ia) {
for (int& col : row) std::cout << col;
std::cout << std::endl;
}