目录
new operator、newoperaot与定位new:
new和delete:
new会返回申请空间的指针.
以下代码段等价:
int main() {
int* n = (int*)malloc(sizeof(int));
assert(n != NULL);
*n = 1;
free(n);
int* m = new int(1);
delete m;
int* n = (int*)malloc(sizeof(int)*4);
assert(n != NULL);
free(n);
int* m = new int[4];
//方括号[]内可以写变量和常数,但是写0或者不写东西的话相当于不创建空间
//c++11支持new时给数组初始化,
//比如:int* m = new int[4]{1,2,3,4,4,5,5,6,9,10};
delete []m;
//delete数组时[]内写不写、写什么都一样没区别.删的都是new时的大小
return 0;
}
在申请自定义类型的空间时,new会先申请空间再调用构造函数,delete会调用析构函数再释放空间,而malloc与free不会调用构造和析构函数:
class test {
public:
test(int n=0) {
cout << "test" << endl;//证明自动调了构造函数
_data = n;
}
~test() {
cout << "~test" << endl;//证明自动调了析构函数
}
void init(int n) {
_data = n;
}
void destroy() {
}
private:
int _data;
};
int main() {
//test t,静态申请,作用域结束时自动析构
test* pt1 = (test*)malloc(sizeof(test));//动态申请,创建无名的test空间,用pt1指向无名空间并操作
assert(pt1 != NULL);
pt1->init(1);
pt1->destroy();
free(pt1);
test* pt = new test(10);//动态申请但是可以自己调用构造函数和析构函数
delete pt;
}
深拷贝与浅拷贝:
浅拷贝:使用默认的拷贝构造方法就是浅拷贝,浅拷贝创造出来的两个对象s和s1的地址虽然并不相同,但是他们的成员char* _str会指向同一空间.
String类如果使用默认的拷贝构造方法或者赋值重载方法使用的是浅拷贝,会导致系统崩溃,因为析构时意味着释放多个相同的地址空间.
如果我们自己定义拷贝构造方法和赋值重载方法,且自己申请创造空间使用深拷贝(意思就是自己写了new或malloc才是深拷贝)则不会发生此问题....如下图s._str和s1._str的值并不相同.
class String {
public:
String(const char* str="") {
_str = new char[strlen(str) + 1];
strcpy(_str,str);
}
String(const String& s) {
//浅拷贝
_str = s._str;
//深拷贝
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
String& operator=(const String& s) {
if (this != &s) {
delete[]_str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
~String() {
delete []_str;
_str = nullptr;
}
private:
char* _str;
};
int main() {
String s;
String s1=s;
return 0;
}
深拷贝:
浅拷贝:
引用计数类与写实拷贝:
为了提高空间利用的效率又很好的解决浅拷贝析构同一片空间的问题,我们使用引用计数类!
string_rep类里面有计数器_user_count记录一个类实例化的次数. 当_user_count==0时再释放该空间,就解决了上述问题.
注意看代码的注释!
class String;
class String_rep {
friend class String;
public:
String_rep(const char* s="") :_user_count(0)//这里初始化为0是因为string类在构造函数里已经调用了add()
{
_str = new char[strlen(s) + 1];
strcpy(_str,s);
}
//用户在外部只会用string类的拷贝构造和赋值操作
//并不需要使用string_rep的这两个方法,比如s构造时,会构造一个string_rep
//然后s1并不会调用这里内部string_rep类的默认的拷贝构造函数,而是将s._s给s1._s赋值
String_rep(const String_rep& sr);
String_rep& operator=(const String_rep& sr);
~String_rep() {
if (_user_count == 0) {
delete []_str;
_str = nullptr;
}
}
void add() {
++_user_count;
}
void jianjian() {
--_user_count;
}
private:
char* _str;
int _user_count;
};
class String {
public:
//这里构造函数参数用const char*是因为用户初始化是用string类,,不关心内部代码,所以用char*接收实参
String(const char* s="") :_s(new String_rep(s))
{
_s->add();
}
String(const String& sl) {
_s = sl._s;
_s->add();
}
String& operator=(const String& sl) {
if (this != &sl) {
_s->jianjian();
_s = sl._s;
_s->add();
}
return *this;
}
~String() {
_s->jianjian();
if (_s->_user_count == 0) {
delete _s;
_s = nullptr;
}
}
//写实拷贝
void toup() {
String_rep* new_s = new String_rep(_s->_str);
_s->jianjian();
_s = new_s;
_s->add();
char* p = _s->_str;
while (*p != '\0') {
if (*p >= 'a' || *p <= 'z') {
*p-= 32;
}
*p++;
}
}
private:
String_rep* _s;
};
int main() {
String s("abc");
String s1 = s;
String s0("xyz");
String s2 = s0;
String s3;
s3 = s0;
s2.toup();
return 0;
}
调用toup前:
调用写实拷贝方法toup后:
new operator、newoperaot与定位new:
new operator:
平常我们使用new创建空间就叫new operator或new操作符:
operator new:
之前我们说使用new创建自定义类型时,new会先申请空间再调动构造函数,这里先申请空间就是调动operator new(操作符 new,操作符重载)函数
比如我们可以在全局下自己写一个new操作符重载函数:
我们再使用test* p=new test;就可以看到是先调动这个函数再调用拷贝构造方法:
operator new的返回值必须是void*,第一个参数类型必须是无符号整型.
也可以有单独调用operator new而不调用拷贝构造函数:
使用operator new创建空间时,如果类内自己重载了就调用自己类内的,没有再调用全局的,再没有才调用系统的.
对应的还有operator delete
定位new:
定位new可以指定空间的指定位置赋值,所以说不是所有的new都会创造申请空间.