开始学习C++

关于C和C++之间的过渡知识。

第一个C++程序

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
int main()
{
using namespace std;

cout << "Please enter a Celsius value:";
double c, f;
cin >> c;
f = c*1.8 + 32.0;
cout << c << " degrees Celsius is " << f << " degrees Fahrenheit" << endl;

return 0;
}

用new来分配空间

new

我们知道在C语言里用malloc()来分配内存;在C++仍然可以这样做,但C++有更好的办法——new运算符。

为一个数据对象(结构、基本类型等等)获得并指定分配内存的格式:

1
2
ElementType * pointer_name = new ElementType;
int *p1 = new int;

那么,这两种方法,将变量的地址赋给指针,有什么区别呢?

1
2
3
4
5
6
7
8
9
10
#include<iostream>
int main()
{
using namespace std;

int *p1 = new int;

int a ;
int *p2 = &a;
}

第一种情况是new将找到一个长度正确的内存块,返回该内存块的地址。
第二种情况下,可以用变量名a来访问该int,而第一种情况则只能通过指针来访问。
乍一看,使用new方法处理数据可能不太好用(p1指向的“东西”没有名称),但是它使得程序在管控内存方面有更大的控制权。

如果像这样,程序只需要一个值,或者一个简单的变量,这样的管理一个小型数据对象来说,直接声明比new或者指针更简单(此时的指针不让人印象深刻)。通常,对于大型数据(如字符数组、结构),应该使用new和指针,这正是指针的用武之地。

使用delete释放内存

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
int main()
{
using namespace std;

int *p1 = new int;
delete p1; //valid and necessary

int a = 5;
int *p2 = &a;
delete p2; //not alowed, memory not allocated by new
}

内存被耗尽?

可能由于没有足够的内存满足new的要求,这种情况下,new函数将会有错误处理。在较老的实现中,new将返回0,值为0的指针就是空指针(NULL)。C++确保空指针不会指向有效的数据,它常用来表示函数失败(如果成功,返回的是一个有用的指针)

内存泄漏?(memory leak)

1
2
int *p1 = new int;
delete p1;

这会释放p1的内存,但是不会删除p1本身。例如,delete p1后,可以对p1再使用new分配新内存。一定要配对使用new和delete,否则可能发生内存泄漏,也就是说,被分配的内存无法再被使用。如果这种情况严重,则程序将由于寻找不到内存而终止。

new创建动态数组

1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
int main()
{
using namespace std;

/*new and delete*/
int *p1 = new int[10];
delete[]p1;

return 0;
}

delete中的方括号告诉程序:释放整个数组。
new里有括号,delete就要带括号,new里没有,则delete也没有。

总之,使用new和delete的时候,遵守以下规则:(和malloc和free的原则有些许类似)

  1. 不要使用delete去释放不是new分配的内存。
  2. 不要对同一个内存块delete两次。
  3. 如果用new[],则应该使用delete[]。
  4. 如果用new(没有方括号),则应该使用delete(没有方括号)
  5. 对空指针使用delete是安全的。

函数与数组

在函数里声明数组:

1
int sum_arr(int arr[], int n)

这看似合理,方括号指明arr是一个数组,但实际情况是:arr实际上不是一个数组,而是一个指针。

这样是有好处的,将数组地址作为参数传递可以节省复制整个数组所花费的时间。而且如果数组很大,则使用拷贝时系统内存开销非常大。不仅需要占用很多内存,还需要很多时间去复制,还使得原始数据增加了被破坏的风险。

1
2
3
4
5
6
int a[5] = {1,2,3,4,5};
int *p = a;

cout<<sizeof(a)<<endl;
cout<<sizeof(*a)<<endl;
cout<<sizeof(p)<<endl;

上述代码输出结果是20、4、4。

const用法

用const保护数组数据

为了确保显示函数不修改数组原始数据,除非函数目的在此,否则应避免这种情况的发生。为防止函数无意中修改了数组的内容,可以在声明形参的时候使用关键字const

1
void show_array(const double ar[], int n);

假设函数更改了数组内容,编译器会报错。所以说,const主要是为了防止程序员的错误。

const与指针

const用于指针:

指向常量的指针

让该指针指向一个常量对象,这样可以防止用该修改指针来修改所指向的常量值。

1
2
3
4
5
int a = 2, b = 4;
const int *p = &a;

*p = 0;//invlaid
p = &b;//valid

常指针

将指针本身设置为常量,指针指向一个对象后,就一直效忠于这个对象。可以防止改变指针所指向的位置。

1
2
3
4
5
int a = 2, b = 4;
int const *p = &a;

*p1 = 2;//valid
p1 = &b;//invalid

通用的方法:对于指针的定义语句,从右往左读。例如语句const int *p就是:定义一个指向int型常量的指针。对于int const *p,就是:定义一个常指针,指向一个int。

尽可能的使用const

将指针参数声明为指向常量数据的指针有两个好处:

1.这样可以避免无意间的修改而导致的错误。
2.使用const可以让函数处理const和非const形参,否则只能接受非const形参。