在C++中,内存分为栈和堆。栈中的对象生命周期较短,往往在作用域结束后就会销毁,而堆中的对象生命周期较长,只有当使用delete或者程序结束时才会销毁。而new则是将我们创建的对象分配到堆中,使对象可以跨作用域使用。
部分引用类型的类成员就会用到new,例如String中的char*指针,这样类就可以处理不同长度的字符串,而不是在类设计时固定一个数组的长度,但是在类构造中使用new一定要记得释放和同步,如果构造函数中使用new则在析构函数用要使用delete。
String::String(){ stp = new char[length + 1];}
String::~String(){ delete stp; stp = NULL;}
这个还挺重要的,因为在C++中不使用new也可以创建类对象,但是是分配在栈中的,这样在使用指针的时候就会出现一些问题。
String function(String stp){
String myString; //隐式调用默认构造函数在栈中创建一个myString对象
stp = myString;
return stp;
} //当该作用域结束时,该对象会被销毁,而其也会相应的调用其析构函数
在上述例子中,我们是可以获得一个返回的String对象的,但是当调用其中的指针读取字符时则会出现问题,首先:
1.当需要赋值时会创建一个临时变量,然后调用复制构造函数将myString对象中的成员对象复制到临时变量中。
2.在使用复制构造函数时,只会简单的进行浅拷贝,对于基本类型是值拷贝,而引用类型则是地址的拷贝。
3.赋值运算符还是会进行一次对象的赋值,所以还需要重写赋值运算符,因为不清楚程序是否会调用还是保险为好。
当使用stp = myString;时
可能会用到复制构造函数,也可能用到赋值运算符,在这种情况下可以使用两者都进行重写。
重写复制构造函数:
Class_name(const Class_name&);
重写赋值运算符:
Class_name& Class_name::operator=(cosnt Class_name&);
例子:
String(const String& str){
delete[] this->stp;
this->stp = new char[length + 1];
for(int i = 0; i < size;i++){
this->stp[i] = str.stp[i];
}
}
使用上述构造函数和赋值运算的重写,将类对象中的引用类型进行值的拷贝,这样当赋值时临时对象的销毁就不会引起空指针或者内存泄漏的问题。
C++构造函数提供了一种可以用来初始化数据成员的特殊语法,就是使用成员初始化列表。
语法:
String::String(int qs):qSize(qs),items(0),front(NULL),real(NULL){
}
这种方式可以初始化非静态的const对象。
或者直接在类中定义初始化
class String{
private:
enum{Q_SIZE = 10};
Node* front = NULL;
int items = 0;
const int qSize = Q_SIZE;
}
将一个标准类型变量赋给另一种标准类型变量时,如果两者类型兼容,则C++自动将这个值转换为接收变量的类型。
只接受一个参的构造函数才能作为转换函数。
String(double db);
String(int i);
String(char a);
当声明以上构造函数时,使用下面方式会进行隐式的调用对应的构造函数,将其他类型转为String类型对象.
String a = 1; //调用形参为int的构造函数
String b = 23.2; //double
function(String str);
function(1); //也会转换为String对象
但是这样会产生一部分问题,当并不想隐式的执行转换函数时,需要使用explicit关键字来关闭这种自动特性。
explicit String(int i); //不能隐式的作为转换。
String a = 1 //错误,不能使用隐式转换
String a = (String)1; //正确
String a = String(1); //正确
类构造器也支持自动转换,允许可以转型的对象进行自动的转换.
例如,只持有一个double型参的构造函数:
String(double db);
String a = 1; //将int类型转换为double类型然后创建对象。
但是自动转换不能有二义性,如果String构造函数中还存在其他可以由int转型的类型如long,则会出现错误,编译器将提示错误。
既然可以将其他类型转换为类对象,也可以将类对象转换为特定的一种类型,需要使用到转换函数。
需要注意的是:
1.转换函数为类方法
2.不允许有返回类型
3.不能有型参
语法:
operator double();
例子:
explicit operator int(){ //使用explicit关键字,防止隐式转化
int length = this->size;
int sumNum = 0;
int dig = 1;
for(int i = 0; i < length; i++){
sumNum += (this->stp[i] - '0') * dig;
dig *= 10;
}
return sumNum;
}
在上述例子中,将字符数组中的数字进行计算,得出了一个int值,实现了将String对象转换为int类型。