在C++中也是少不了对内存的管理,在C++中只要有new的地方,在写代码的时候都要想着delete。 new分配的时堆内存,在函数结束的时候不会自动释放,如果不delete我分配的堆内存,则会造成内存泄露。所以我们要学会内存管理,不要内存泄露。在C++中的内存管理机制和OC中的还不太一样,在OC中的ARC机制会给程序员的内存管理省不少事,但在C++中没有ARC所以我们要自己管理好自己开辟的内存。Java中也有自己相应的内存管理机制,比如JDBC里的获取的各种资源在finally里进行close等
那么什么情况下我们写的程序会出现内存泄露呢?下面我们将会举一个简单的例子来分析一下C++中的内存管理机制。
1.建立一个测试类TestClass, TestClass类中有一个私有的属性(指针类型),有一个无惨的构造函数,有一个析构函数,还有一个display方法用于输出对象的信息
测试类的声明如下:
//内存管理的测试类
class TestClass
{
private:
char *name;
public:
//无参构造函数
TestClass();
//析构函数
~TestClass();
//描述方法
void display();
};
在xxx.cpp文件中定义类的实现方法
实现构造方法,实现构造方法时要给属性指针分配空间,不然的话属性指针没有分配内存地址,调用时程序会崩溃,用new方法进行堆分配
//无参构造函数
TestClass::TestClass()
{
cout << "TestClass()" <<endl;
//给指针属性分配内存(堆分配)
this->name = new char[255];
//进行初始化
strcpy(this->name, "ludashi");
}
实现析构函数在析构函数中要对构造函数中堆分配的内存进行delete,不然会造成内存泄露
//析构函数
TestClass::~TestClass()
{
delete [] this->name;
cout << "~TestClass()" << endl;
}
实现display函数,进行name的打印测试
//描述方法
void TestClass::display()
{
cout << this->name <<endl;
}
2. 在main函数中进行测试
实例化对象时进行堆分配:需要手动进行内存的释放,不然也会造成内存的泄露
//TestClass类的初始化,堆分配,需要delete
TestClass * testClass = new TestClass();
实例化对象的栈分配:不需要手动释放内存,大括号结束时就自动释放栈内存
//栈分配,不用delete,出大括号后自动释放
TestClass stackClass = TestClass()
信息的打印输出
testClass->display();
调用delete来释放堆分配的对象
delete testClass;
3.程序运行结果:如果不加delete testClass; 析构函数只会调用一个,因为堆分配的对象不会自动释放,需要手动释放,不加则会造成内存的泄露
TestClass()
TestClass()
ludashi
~TestClass()
~TestClass()
4.拷贝构造函数
如果在main函数中加入下面这句话,程序在运行时就会崩掉,如果要想程序正常运行可以把析构函数中的delete [] this->name;注释掉就可以运行。不过这样会引起内存的泄露。那么我们来研究一下为什么加上下面这句话程序会崩掉呢?原因是下那句话的意思是copyTest和stackClass指向同一块栈内存,当其中一个调用析构函数时就会把name给delete掉,另一个在析构调用delete时就会报错。怎么从基本上解决问题呢?接下来就是拷贝构造函数出场的时候啦。
TestClass copyTest = stackClass;
下面是拷贝构造函数的定义方法
//拷贝构造函数
TestClass::TestClass(const TestClass &test)
{
//在堆中分配新的内存
this->name = new char[255];
//进行拷贝
strcpy(this->name, test.name);
}
在main函数中调用拷贝构造函数 ,这样的代码有不会有刚才的问题了
TestClass copyTest = stackClass;
5.再提内存管理,不禁又想到初学C++那会的一句话“先构造的后析构”;有new的地方就得想着delete,为了避免内存泄露。
上面的拷贝构造函数的作用是在声明对象的时候可以利用拷贝构造函数给新的对象赋值,如果像下面的这种情况就会出现过度释放的问题;
TestClass test1 = TestClass();
TestClass test2 = TestClass();
test2 = test1;
接下来就该操作符重载出场的时候了(operator = )把=号进行重载
//对象之间的赋值:操作符重载
TestClass & TestClass :: operator = (const TestClass &test)
{
strcpy(this->name, test.name);
return *this;
}