总结一些C++学习中疑惑的点

知识点

const和指针

  • 指向常量的指针:不能用于改变其所指对象的值。
1
2
3
4
const double pi=3.14;
const double *p=π//pi必须要用指向常量的指针指向
double dval=3.14;
p=&dval; //可以,但是不能使用p改变dval的值

存放常量对象的地址必须要使用指向常量的指针,

但是指向常量的指针也可以指向变量

  • const指针:常量指针必须初始化,常量指针指向的对象地址不可改变。
1
2
3
4
int a=0;
int *const p=&a; //p将一直指向a
const doublie pi=3.14;
const double *const pip=π //pip是指向常量对象的常量指针
  • 顶层const:表示指针本身是个常量
  • 底层const: 表示所指的对象是个常量

指针指向对象,指针在顶层,对象在底层。

1
2
3
4
5
int v1=0;
int *p1=&v1;
const int *p2=p1; //对,因为int*可以转化为const int*
p1=p2; //不对,因为p2是底层const,p1不是。

当执行对象拷贝时,拷入拷出的对象必须具有相同的底层const资格。或者两个对象数据类型必须可以转换。非常量可以转换成常量,反之不行

底层指针的拷入主要看是否同为底层的或者是非常量。

常量指针的拷入只能拷入常量。

顶层指针因为只能初始化一次后不能改变所以不做讨论。

constexpr

1
2
const int *p=nullptr; //底层const
constexpr int *q=nullptr;//顶层const 相对于*const

数组

引用遍历法:

要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。

1
2
3
4
5
6
int ia[row][col];
for(auto &row:ia){
for(auto i:row){
cout << i; //i只读,改变对数组没有影响。
}
}

但是想要去更改数组里面的值就要使用引用类型去遍历

1
2
3
4
5
for(auto &row:ia){
for(auto &col:row){
//do something
}
}

下标遍历法:

1
2
3
4
5
6
int ia[row][col];
for(size_t i=0;i!=row;++i){
for(size_t j=0;j!=col;++j){
ia[i][j]=1;
}
}
1
2
int i=0,*p=&i;
*p=2;//*p是i的一个引用,相对于&j=i;

指针遍历法:

1
2
3
4
5
for(auto p=ia;p!=ia+3;++p){
for(auto q=*p;q!=*p+4;++q){
...
}
}

其中p中存放地址是ia[x]的地址而且ia[x]和ia[x+1]的地址是连续的,而*p是取ia[x][0]的地址。

左值和右值

拷贝构造函数

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。当使用拷贝初始化时,我们会用到拷贝构造函数。

1
2
3
4
5
class Foo{
public:
Foo(); //默认构造函数
Foo(const Foo&);//拷贝构造函数
}

拷贝函数通常不应该是explicit的

拷贝初始化

  • 用=定义变量

  • 将一个对象作为实参传递给一个非引用类型的形参

  • 从花括号列表初始化一个数组中的元素或一个聚合类中的成员

重载拷贝赋值运算符

如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符

如果一个运算符是一个成员函数,其左侧运算对象就绑定到隐式的this参数。赋值运算符通常返回一个指向其左侧运算对象的引用。

练习

9.43

如果使用!=来比较两个迭代器在这题是不行的,但是如果不是特殊条件最好写成!=的判断条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
using namespace std;

void replace(string& s, const string& oldVal, const string& newVal)
{
auto curr = s.begin();
while (curr <= s.end() - oldVal.size())
{
if (oldVal == s.substr(curr-begin(s), oldVal.size()))
{
curr = s.erase(curr, curr + oldVal.size());
curr = s.insert(curr, newVal.begin(), newVal.end());
curr += newVal.size();
}
else
{
++curr;
}
}
}
int main() {
string s = "tho ";
string oldVal = "tho";
string newVal = "though";
replace(s, oldVal, newVal);
cout << s;
return 0;
}

9.44

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

void replace(string& s, const string& o, const string& n)
{
size_t i = 0;
while (i+o.size() <= s.size()) {
if (o == s.substr(i,i+o.size())) {
s.replace(i, o.size(), n);
i += n.size();
}
else {
i++;
}
}
}
int main() {
string s = "tho ";
string oldVal = "tho";
string newVal = "though";
replace(s, oldVal, newVal);
cout << s;
return 0;
}