首先说说constructors(建构子)这个类的特殊的成员函数。
当一个类的instance被创建时, 会呼叫建构子以对对象的数据初始化。
例如如下例:
声明一个整数的类:
#include <iostream>
using namespace std;
class Integer {
private:
int val;
public:
Integer() {
val = 0;
cout << "default constructor" << endl;
}
}; //分号不能少
int main() {
Integer i;
return 0;
}
运行结果为:
不难发现, 上述主程序中子调用了默认建构子。
当我们创建an array of objects的时候, 默认建构子会逐一调用(必须有默认建构子, 才可以创建an array of objects)。
#include <iostream>
using namespace std;
class Integer {
private:
int val;
public:
Integer() {
val = 0;
cout << "default constructor" << endl;
}
}; //分号不能少
int main() {
Integer arr[3];
return 0;
}
程序运行结果为:
当在一个类中使用了另一个类声明的对象作为其成员变量, 此时城建这个类的对象的时候, 也会调用内部那个嵌入类的constructor, 以便对其初始化。
例:
#include <iostream>
using namespace std;
class Integer {
private:
int val;
public:
Integer() {
val = 0;
cout << "Integer default constructor" << endl;
}
}; //分号不能少
class IntegerWrapper {
private:
Integer val;
public:
IntegerWrapper() {
cout << "IntegerWrapper default constructor" << endl;
}
};
int main() {
IntegerWrapper q;
return 0;
}
运行结果为:
Constructor 也可以有参数, 但是此时该建构子不是默认的construtor, 以便传递参数更好地初始化。 此时, 我们必须定义(写明)默认的建构子。 否则默认的建构子 not available。 如果不写明默认建构子, 一个缺点就是无法什么 array of objects without initializing了。
例如, 下面的两个程序是错误的:
#include <iostream>
using namespace std;
class Integer {
private:
int val;
public:
Integer(int v) {
val = v;
cout << "Integer default constructor" << endl;
}
}; //分号不能少
int main() {
Integer i(3);//ok
Integer j;// error
return 0;
}
上述代码会出错, 再例如:
#include <iostream>
using namespace std;
class Integer {
private:
int val;
public:
Integer(int v) {
val = v;
}
}; //分号不能少
int main() {
Integer a[] = {Integer(2), Integer(5)};//ok, initializing
Integer b[2];// error, without initializing
return 0;
}
下面修改如上的代码, 两种办法, (1)单独写一个默认的建构子(默认的就是没有参数的)
(2)采用函数的default 用一个建构子代表两个建构子(一个有参数, 一个是无参数的默认建构子):
#include <iostream>
using namespace std;
class Integer {
private:
int val;
public:
Integer(int v) {
val = v;
}
Integer() {
val = 0;
}
}; //分号不能少
int main() {
Integer a[] = {Integer(2), Integer(5)};//ok, initializing
Integer b[2];//okay
return 0;
}
上述的代码是correct的。
this 指针
当一个method的参数的名字和类的一个field同名的时候, 如何去区分呢, 这就用到了this指针。 this指向当前调用这个方法的对象的位置。
例如:
class Integer {
private:
int val;
public:
Integer(int val = 0) { //default parameter
this -> val = val;
}
void setVal(int val) {
this -> val = val;
}
}; //分号不能少
接下来, 说说Scoping and Memory
每当我们声明一个新的变量的时候(int x), 编译器就会给这个变量x 分配(allocate)一个内存。 当这个变量go out of scope 的时候, 这个内存就会被释放掉(be freed up), 以便这个内存位置可以分配给其他变量。
例如:
int main() {
if (true) {
int x = 5; //declare a variable, allocate memory
}
// x now out of scope, memory it used to occupy can be reused
}
一句话, 当一个variable goes out of scope, that memory is no longer guaranteed to store the variables value。
在举一个例子:
int main() {
int *p;
if (true) {
int x;
p = &x;
}
cout << *p << endl; // ???, x is out of scope, so it will get wrong answer
return 0;
}
上个例子会出错。
具体分析内存分配如下面几幅图:
上图中, 最后一幅图不难看出, 变量x goes out of scope, 所以该内存(用于存储5的memory)被释放掉了。 也就是说, 此时指针p变成了一个dangling pointer(也就是说指针指向的memory的contents 是未定义的(undefined))。
A problematic Task
Implement a function which returns a pointer to some memory containing the integer 5.
下面的实现的函数是incorrect的,原因是变量x 是在function scope 中, 该函数返回的时候, x 就会go out of scope。 所以此时返回的指针就变成了一个dangling pointer。
int *getPtrToFive() {
int x = 5;
return &x;
}
int main() {
int *p = getPtrToFive();
cout << *p << endl; //??
}
上述函数的实现方法是不正确的。 走出这个dilemma的方法就是我们动态的分配内存。这种动态分配的内存会一直remian 下去直到我们manually de-allocate it。
动态分配内存用到了new operator,此时返回一个pointer 指向the newly allocated memory. 如下句:
int *x = new int;
NOTE:
——如果使用 int x; 分配的内存区域在the stack (栈)中
——如果使用 new int; 分配的内存区域在the heap (堆)中。
要释放由 new operator 分配在heap 中的内存, 必须使用delete operator 手动释放的。 (这两个运算符需要配套使用, 使用完毕后, 用delete释放动态内存)。
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
cout << *p << endl; //5
delete p;
}
注意, 如果使用完后, 不用delete 去deallocate由new 分配的动态内存, 那么我们编写的application 就会waste memory。
如下例:
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
for (int i = 0; i < 3; ++i) {
p = getPtrToFive();
cout << *p << endl;
}
}
具体分析如下:
上述的程序错误的原因在于没有deallocate 分配的动态内存。
再比如, 下面的例子, 虽然delete了, 但是只是deallocate 最后一次分配的动态、内存:
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
for (int i = 0; i < 3; ++i) {
p = getPtrToFive();
cout << *p << endl;
}
delete p;
}
上述的语句时不正确的, 因为它导致了memory leak(内存泄露)。 要想修正这个错误, 从而避免内存泄露, 把delete的语句放在loop内:
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
for (int i = 0; i < 3; ++i) {
p = getPtrToFive();
cout << *p << endl;
delete p;
}
return 0;
}
注意, 删除heap中的动态内存之后, 就不要再使用分配的内存了:
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
delete p;
cout << *p; // error
}
必须在使用之后, 不再需要了, 才删除:
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
cout << *p << endl; //5
delete p;
return 0;
}
注意, 同一个内存空间删除两次:
int *getPtrToFive() {
int *x = new int;
*x = 5;
return x;
}
int main() {
int *p = getPtrToFive(); //动态分配内存的时候对指针的初始化
cout << *p << endl; //5
delete p; delete p; // error
return 0;
}
只有用new 分配的动态内存才可以使用delete operator:
int main() {
int x = 5;
int *xPtr = &x;
cout << *xPtr << endl;
delete xPtr; //error
return 0;
}
上面是错误的, 应该使用:
int main() {
int x = 5;
int *xPtr = &x;
cout << *xPtr << endl;
}
Allocating Arrays
在stack 中声明数组时(分配内存), 我们必须指定数组的size 为常量, 不能为变量。也就是说:
int arr[SIZE];// size 必须为常量
例如, 下面的语句时错误的:
int numItems;
cout << "How many items?";
cin >> numItems;
int arr[numItems]; // not allowed
要想实现动态的分配数组的内存, 必须使用 new [] , 这时候, 数组可以有variable size, 例如:
int numItems;
cout << "How many items?";
cin >> numItems;
int *arr= new int[numItems];
具体分析如下:
删除动态数组的内存, 使用的是 delete[], 如下:
int numItems;
cout << "How many items?";
cin >> numItems;
int *arr= new int[numItems];
delete[] arr;
下面举个例子:
#include <iostream>
using namespace std;
int main() {
int numItems;
cout << "How many items?";
cin >> numItems;
int *arr = new int[numItems];
for (int i = 0; i < numItems; ++i) {
cout << "Enter item" << i << ":";
cin >> arr[i];
}
for (int i = 0; i < numItems; ++i) {
cout << arr[i] << endl;
}
delete[] arr;
return 0;
}
运行结果如下: