C++中的new和delete在类中的应用

本文详细解释了C++类中构造函数和析构函数的作用,特别是针对指针成员的内存管理。通过实例演示了如何避免在对象赋值时出现的内存泄露问题,并介绍了浅拷贝与深拷贝的概念,以及如何在类中正确实现拷贝构造函数以确保数据的安全转移。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类的构造函数和析构函数的一个典型应用就是:在构造函数中用new为指针成员开辟独立的动态内存空间,而在析构函数中用delete来释放它们。

一个类的成员的数据类型可以是任何有效的合法类型,当数据类型为指针时,这样的成员称为指针成员,注意有潜在的危险性。

class CName

{

public:

CName()    // A:显式默认构造函数

{

strName = NULL;

}

CName(char *str)  // B: 

strName = str;

}

~CName()  { }   //显式默认析构函数

char *getName()

{

return strName;

}

private:

char *strName;

};

int main()

{

char *p = new char[5]; // 为p开辟内存空间

strcpy(p, "DONG"); // p指向的内存空间值为 "DONG"

CName one(p); // 对象初始化

delete []p; // 释放P的内存空间

cout<<one.getName()<<endl;

return 0;

}

以上代码会出现问题,由于 ” CName one(p) “,调用的是B重载构造函数,从而使得私有指针成员 strName指向等于p的指向,而p指向的是new开辟的内存空间,其内容是”DONG“,一旦p指向的内存空间删除,p的指向就不确定了,此时,strName指向也不确定。

为了保证类的封装性,类中的指针成员指向的内存空间必须在类中自行独立开辟和释放。

改进后的代码:

class CName
{
public:
CName()    // A:显式默认构造函数
{
strName = NULL;
}
CName(char *str)  // B: 

//strName = str;
//在类的内部自行开辟内存空间
strName = (char *)new char[strlen(str)+1];
strcpy(strName,str);

}
~CName()  
{
if (strName)
{
delete []strName;
strName = NULL;

}
}   //显式默认析构函数
char *getName()
{
return strName;
}
private:
char *strName;
};
int main()
{
char *p = new char[5]; // 为p开辟内存空间
strcpy(p, "DONG"); // p指向的内存空间值为 "DONG"
CName one(p); // 对象初始化
delete []p; // 释放P的内存空间
cout<<one.getName()<<endl;
return 0;
}



对象赋值和拷贝

class CName;

CName o1("DONG"); // A:通过构造函数设定初值

CName o2(o1); //B:通过指定对象设定初值

B语句通过将o1作为o2的初值,o2的初始化形式要调用相应的构造函数,事实上,CName隐含一个特殊的默认构造函数,其原型为:CName (const CName &);这种特殊的默认构造函数称为:默认拷贝构造函数。在C++中,每个类总有一个默认拷贝构造函数,目的是保证B语句中对象初始化形式的合法化,功能等价于:CName o2=o1;

没有指针成员时的情况是:

class CData

{

public:

CData( int data=0 )

{

m_nData = data;

}

~CData()  { }

int getData()

{

return m_nData;

}

private:

int  m_nData;  //非指针型成员变量

};

下面的对象初始化形式是合法的

CData c1(3); // 通过重载构造函数设定初值

CData c2(c1); // 通过默认拷贝构造函数设定初值

//等价于 CData c2=c1;

同类型的变量可以直接用 ” = “ 来赋值,对于CData c2=c1; C++中赋值运算符的操作就是:将右操作对象的内容拷贝到左操作对象的内存空间中去。


有无 指针成员的区别在哪里?

CName的数据成员strName是一个” char * “指针,其自身内存空间存放的是指针的地址,因而其数据的存储还需另外开辟一个不依附于外部的独立的内存空间,由于m_nData自身的内存空间就是用来存储数据的,因此CData对象初始化所进行的数值拷贝是有效的。


浅拷贝

定义:每一个C++类都有一个隐式的默认拷贝构造函数,目的是保证类对象拷贝初始化方式的合法性,其功能是将一个已定义的对象所在的内存空间的内容依次拷贝到被初始化的对象的内存空间中去,这种内存空间内容的拷贝方式称为--浅拷贝。


深拷贝

定义:对于数据成员有指针类型的类来说,都会出现上述CName的问题,由于默认拷贝构造函数无法解决,必须自己定义一个拷贝构造函数,在数值拷贝之前,需要为指针类型的数据成员另辟一个独立的内存空间,因而称为--深拷贝


对于类CName的拷贝构造函数,可以有的合法声明方式有:

CName (CName &x);

CName (const CName &x);

CName (CName &x, ...);

CName (const CName &x, ...);

一旦类中定义了拷贝构造函数,隐式的默认拷贝构造函数和隐式的默认构造函数就失效了!因为拷贝构造函数也是构造函数一种。










<think>嗯,用户问的是C++newdelete作为运算符的原因,以及它们的原理作用。首先,我需要回顾一下C++的内存管理机制。在C语言中,我们通常用mallocfree来动态分配内存,但C++引入了newdelete作为运算符,这应该是有原因的。可能的原因包括型安全、构造析构函数的自动调用,还有运算符重载的可能性。 接下来,我得理清楚newdelete的基本原理。当使用new时,它不只是分配内存,还会调用构造函数初始化对象。这跟malloc不同,malloc只是分配内存块而不处理对象的构造。同样,delete会调用析构函数并释放内存,而free只是释放内存。这可能就是C++引入这两个运算符的主要原因之一,为了更好的对象生命周期管理。 然后要考虑运算符重载。用户提供的引用里提到,用户可以重载operator newoperator delete,这允许自定义内存分配策略,比如使用内存池或者进行性能监控。这应该是C++设计中的一个重要考虑,提供了灵活性。 型安全也是一个点。new返回的是正确型的指针,而malloc返回void*需要强制转换,容易出错。C++强调型安全,所以用new能减少这错误。 另外,异常处理方面,如果new分配失败,默认会抛出std::bad_alloc异常,而malloc失败返回NULL,这可能需要不同的错误处理方式。不过用户提到的引用里说可以检查返回值是否为0,这可能是在旧的标准或编译器中的行为,现在通常是抛异常,但可以通过nothrow版本来避免。 用户提供的引用中还提到,delete一个未分配的指针会导致未定义行为,但delete空指针是安全的。这一点需要明确说明,避免用户误用。 可能用户的实际需求是想深入理解C++的内存管理机制,或者在进行底层优化时遇到问题。他们可能想知道为什么C++不像C一样继续使用malloc/free,或者如何正确使用new/delete来避免内存泄漏。此外,运算符重载的应用场景也是他们关心的,比如如何自定义内存分配策略。 需要确认各个引用的内容是否正确应用到回答中。引用1提到operator deletedelete的关系,引用2讲重载全局作用域的使用,引用34关于delete的正确使用潜在问题。这些都需要整合到回答里,特别是原理部分注意事项。 总结一下,回答的结构应该是先解释原因,再分点讲原理作用,然后注意事项,最后相关问题。需要确保每个部分都覆盖到用户的问题,并且引用合适的资料支持。</think>### C++newdelete运算符的解析 #### 一、作为运算符的原因 1. **统一内存管理语义** C++将`new``delete`设计为运算符而非函数,是为了与对象生命周期管理紧密结合。运算符形式允许隐式调用构造函数析构函数,例如: ```cpp MyClass* obj = new MyClass(); // 分配内存并调用构造函数 delete obj; // 调用析构函数并释放内存 ``` 这种设计使内存分配与对象初始化/销毁逻辑统一[^1][^3]。 2. **支持运算符重载** C++允许用户重载`operator new``operator delete`,实现自定义内存管理策略(如内存池、性能统计等)[^2]。例如: ```cpp void* operator new(size_t size) { return customAllocator(size); } ``` 3. **型安全** `new`返回具体型指针(如`int*`),而C的`malloc`返回`void*`需强制转换,易引发型错误。 #### 二、原理与作用 1. **`new`的工作流程** - 调用`operator new`分配内存(可自定义) - 在分配的内存上调用构造函数 - 返回型化指针 例如:`int* p = new int(5);`分配4字节内存并初始化为5[^3]。 2. **`delete`的工作流程** - 调用析构函数(若对象存在) - 调用`operator delete`释放内存(可自定义) 例如:`delete[] arr;`释放数组内存并逐个调用析构函数[^4]。 3. **与底层函数的关联** `new`/`delete`底层通过`operator new`/`operator delete`函数实现,默认使用全局版本(`::operator new`)[^2]。 #### 三、关键注意事项 1. **配对使用规则** - `new`与`delete`配对,`new[]`与`delete[]`配对 - 错误示例:`int* arr = new int[10]; delete arr;`(未调用数组析构函数) 2. **空指针安全性** `delete nullptr`是安全的,但重复释放或释放非动态内存会导致未定义行为[^4]。 3. **异常处理** `new`失败时默认抛出`std::bad_alloc`异常,可通过`new (nothrow)`禁用异常。 #### 四、设计意义总结 通过运算符形式,C++实现了: - 对象生命周期自动管理 - 型安全的动态内存分配 - 灵活的内存策略扩展能力
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值