【面向对象】类和动态内存分配

您希望下个月的早餐、午餐和晚餐吃些什么?在第三天的晚餐喝多少盎司的牛奶?在第15天的早餐中需要在谷类食品添加多少葡萄干?如果您和大多数人一样,就会等到进餐时再做决定。C++在分配内存时采取的部分策略与此相同,让程序在运行时决定内存分配,而不是在编译时决定。这样,可以根据程序的需要,而不是根据一系列严格的存储类型规则来使用内存。C++使用new和delete运算符来动态控制内存。遗憾的是,在类中使用这些运算符将导致许多新的编程问题。在这种情况下,析构函数是必不可少的,而不再是可有可无的。有时候,还必须重载赋值运算符,以保证程序正常运行。下面来看一看这些问题。

下面的程序中我们将使用new和delete。并且,引入了一种新的存储类型:静态类成员。首先设计一个StringBad类,然后设计一个功能稍强的MyString类。StringBad类对象将包含一个字符串指针和一个表示字符串长度的值。这里使用StringBad类,主要是为了深入了解new、delete和静态类成员的工作原理。因此,构造函数和析构函数调用时将显示一些信息,以便您能按照提示完成一些操作。

那么为什么将它命名为StringBad呢?这是为了表示提醒,StringBad是一个还没有开发好的示例。这是使用动态内存分配来开发的第一步,它正确地完成了一些显而易见的工作,例如,它在构造函数和析构函数中正确的使用new和delete。它其实不执行有害的操作,但省略了一些有益的功能,这些功能是必需的,但却不是显而易见的。通过说明这个类存在的问题,有助于稍后将它转换为一个功能更强的MyString类时,理解和牢记所做的一些并不明显的修改。

#ifndef STRINGBAD_H_
#define STRINGBAD_H_

#include <iostream>
using namespace std; 

class StringBad
{
	private:
		char * str; 				// pointer to string
		int len; 					// length of string
		static int num_strings; 	// number of objects
		
	public:
		StringBad(const char * s); 	// constructor
		StringBad(); 				// default constructor
		~StringBad(); 				// destructor
		
		// friend function
		friend ostream & operator << (ostream & os, const StringBad & st); 
}; 

#endif
对这个声明,需要注意的有两点:

首先,它使用char指针(而不是char数组)来表示姓名。这意味着类声明没有为字符串本身分配存储空间,而是在构造函数中使用new来为字符串分配空间。这避免了在类声明中预先定义字符串长度。

其次,将num_strings成员声明为静态存储类。静态类成员有一个特点:无论创建多少个对象,程序都只创建一个静态类变量副本。也就是说,类的所有对象共享一个静态成员,就像家中的电话可供全体家族成员共享一样。这对于所有类对象都具有相同值的私有数据是非常方便的。例如,num_strings成员可以记录所创建的对象数目。

下面的程序清单中类方法的实现,它演示了如何使用指针和静态成员。

#include "stringbad.h"
#include <cstring>

int StringBad::num_strings = 0; 

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

StringBad::StringBad()
{
	len = 4; 
	str = new char [len+1];
	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; 
}

ostream & operator << (ostream & os, const StringBad & st)
{
	os << st.str; 
	return os; 
}
首先,请注意下面的语句:

int StringBad::num_strings = 0; 
这条语句将静态成员num_strings的值初始化为0。请注意,不能在类声明中初始化静态成员变量,这是因为声明描述了如何分配内存,但却并不分配内存。您可以使用这种格式来创建对象,从而分配和初始化内存。对于静态类成员,可以在类声明之外使用单独的语句来进行初始化,这是因为静态类成员是单独存储的,而不是对象的组成部分。请注意,初始化语句指出了类型,并使用了作用域运算符,但没有使用关键字static。

初始化是在方法文件中,而不是在类的头文件中进行的,这是因为类声明位于头文件中,程序可能将头文件包括在其他几个文件中。如果在头文件中进行初始化,将出现多个初始化语句副本,从而引发错误。

注意:静态数据成员在类声明中声明,在包含类的方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员是整型const或枚举型const,则可以在类声明中初始化

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值