C++中的动态内存与static

class StringBad
{
private:
	char *str;
	int len;
	static int num_strings;
public:
	StringBad(const char *s);  //comstructor
	StringBad();  //default constructor
	~StringBad();  //destructor      
	friend std::ostream & operator<<(std::ostream & os, const StringBad & st);
	static int HowMany() { return num_strings; }
};

        首先来说这里的static类型。num_strings是静态类成员,静态类成员的特点是,无论创建了多少对象,程序都只创建一个静态类变量副本。也就是说,类的所有对象共享同一个静态类成员。静态数据在类声明中声明,在包含方法的文件中初始化。初始化时使用作用域运算符来指出静态变量所属的类。但静态成员如果是const整数类型或枚举类型,则可以在声明中初始化。

        注意不能在类声明中初始化静态变量,因为声明只是描述了如何分配内存,但并不分配内存。初始化是在方法文件中,而不是在类声明文件中,这是因为静态成员是单独存储的,而不是对象的一部分,而且类声明位于头文件中,程序可能将头文件包括在其他几个文件中。如果在头文件中初始化,将出现多个初始化语句副本,从而引发错误。

        HowMany()是静态类成员函数。首先不能通过对象调用静态成员函数,静态成员函数甚至不能使用this指针。如果静态成员函数是在共有部分声明的,则可以使用类名和作用域解析符来调用,如 int count = StringBad::HowMany(); 。另外,由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。例如,静态方法HowMany()可以访问静态成员num_strings,但不能访问str和len。

StringBad::StringBad(const char * s)
{
	len = std::strlen(s);
	str = new char[len + 1];
	std::strcpy(str, s);
	num_strings++;
	cout << num_strings << ": \"" << str << "\" object created\n";
}
StringBad::StringBad()
{
	len = 4;
	str = new char[4];
	std::strcpy(str, "c++");
	num_strings++;
	cout << num_strings << ": \"" << str << "\" default object created\n";
}

StringBad::~StringBad()
{
	cout << "\"" << str << "\" object deleted, ";
	--num_strings;
	cout << num_strings << " left\n";
	delete[] str;
}

        使用delete删除对象可以删除对象本身所占用的内存,但并不能自动释放属于对象成员的指针指向的内存。因此必须使用析构函数。在析构函数中使用delete语句可确保对象过期时,由构造函数使用new分配的内存被释放。

在构造函数中使用new时应注意的事项

(1)如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete。

(2)new和delete必须互相兼容。new对应delete,new[]对应delete[]。

(3)如果有多个构造函数,则必须以相同的方式使用new,要么都带[],要么都不带。因为只有一个析构函数,所有的构造函数都必须与它兼容。然而,可以在一个构造函数中使用new初始化指针,而在另一个构造函数中将指针初始化为空,因为delete可以用于空指针。

(4)应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象。

(5)应定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象。

new与定位new运算符

扫描二维码关注公众号,回复: 762907 查看本文章
#include<iostream>
#include<string>
#include<new>
using namespace std;
const int BUF = 512;
class JustTesting
{
private:
	string words;
	int number;
public:
	JustTesting(const string & s = "Just Testing", int n = 0)
	{
		words = s; number = n; cout << words << " constructed\n";
	}
	~JustTesting() { cout << words << " destroyed\n"; }
	void Show()const { cout << words << ", " << number << endl; }
};
int main()
{
	char *buffer = new char[BUF];
	JustTesting *pc1, *pc2;

	pc1 = new(buffer)JustTesting;
	pc2 = new JustTesting("Heap1", 20);

	cout << "Memory block address:\n" << "buffer: " << (void *)buffer << "   heap: " << pc2 << endl;
	cout << "Memory contents:\n";
	cout << pc1 << ": ";
	pc1->Show();
	cout << pc2 << ": ";
	pc2->Show();

	JustTesting *pc3, *pc4;
	//fix placement new location
	pc3 = new(buffer + sizeof(JustTesting))JustTesting("Better Idea", 6);
	pc4 = new JustTesting("Heap2", 10);

	cout << "Memory contents:\n";
	cout << pc3 << ": ";
	pc1->Show();
	cout << pc4 << ": ";
	pc2->Show();

	delete pc2;
	delete pc4;
	//explicitly destroy placement new objects
	pc3->~JustTesting();
	pc1->~JustTesting();
	delete[] buffer;
	cout << "Done\n";
	system("pause");

}

对于定位new运算符,程序员必须负责定位new运算符使用的内存单元。要使用不同的内存单元,程序员需要提供两个位于缓冲区的不同地址,并确保这个两个内存单元不重叠。例如

pc1 = new(buffer)JustTesting;
pc3 = new(buffer + sizeof(JustTesting))JustTesting("Better Idea", 6);
delete pc2;
delete pc4;
pc3->~JustTesting();
pc1->~JustTesting();

        而出现上面两种不同方式的原因是delete可与常规new运算符配合使用,但不能与定位new运算符配合使用。delete[]buffer;释放使用常规new运算符分配的整个内存块,但它没有为定位new运算符在该内存块中创建的对象调用析构函数。这种问题的解决方案是,显式地为使用定位new运算符创建的对象调用析构函数。正常情况下将自动调用析构函数,这是需要显式调用析构函数的少数几种情况之一。

        另外,对于使用定位new运算符创建的对象,应以与创建顺序相反的顺序进行删除。原因在于,晚创建的对象可能依赖于早创建的对象。此外,仅当所有对象都被销毁后,才能释放用于存储这些对象的缓冲区。



猜你喜欢

转载自blog.csdn.net/wxn704414736/article/details/80241212