61.动态内存和类
程序清单12.1 strngbad.h
#pragma once
//strngbad.h -- 有缺陷的string类定义
#include <iostream>
#ifndef STRNGBAD_H_
#define STRNGBAD_H_
class StringBad
{
private:
char * str;//指向字符串的指针
int len;//字符串长度
static int num_strings;//对象数量
public:
StringBad(const char* s);//构造函数
StringBad();//默认构造函数
~StringBad();//默认的析构函数
//友元函数
friend std::ostream & operator << (std::ostream & os, const StringBad & st);
};
#endif
StringBad这个类时使用动态内存分配来开发类的第一个阶段,正确地完成了一些显而易见的工作,例如,在构造函数和析构函数中正确地使用了new和delete。这个类没什么错误,但忽略了一些不明显却不可少的东西。
对这个声明,需要注意的有两点。首先,它使用char指针(而不是char数组)来表示姓名。这意味着类声明没有为字符串本身分配存储空间,而是在构造函数中使用new来为字符串分配空间。这避免了在类声明中预先定义字符串的长度。
其次,将num_strings成员声明为静态存储类。静态存储成员有一个特点:无论创建了多少对象,程序都只创建一个静态类变量副本。也就是说,类的所有对象共享同一个静态成员。
程序清单12.2 strngbad.cpp
#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS 1
//strngbad.cpp -- StringBad类方法
#include <cstring>
#include "strngbad.h"
using std::cout;
//初始化static类成员;
int StringBad::num_strings = 0;
//类方法
StringBad::StringBad(const char* s)//构造函数
{
len = std::strlen(s);//设置尺寸
str = new char[len + 1];//分配存储空间
std::strcpy(str, s);//初始化指针
num_strings++;//设置对象数量
cout << num_strings << ":\"" << str
<< "\"对象创建\n";//提示信息
}
StringBad::StringBad()//默认构造函数
{
len = 4;
str = new char[4];
std::strcpy(str, "C++");//默认字符串
num_strings++;
cout << num_strings << ":\"" << str
<< "\" 默认对象创建\n";//提示信息
}
StringBad::~StringBad()//默认的析构函数
{
cout << "\"" << str << "\"对象删除,";//提示信息
--num_strings;
cout << num_strings << " 左\n";
delete[] str;
}
//友元函数
std::ostream& operator << (std::ostream& os, const StringBad& st)
{
os << st.str;
return os;
}
不能在类中初始化静态成员变量,这是因为声明描述了如何分配内存,但不分配内存。可以用这种格式创建对象,从而分配和初始化内存。对于静态成员,可以在类声明之外使用单独的语句进行初始化,这是因为静态成员变量是单独存储的,不是对象的组成部分。
初始化是在方法文件中,而不是在类声明文件中进行的,这是因为类声明位于头文件中,程序可能降头文件包括在其他几个文件中,如果在头文件中进行初始化,将出现多个初始化语句副本,从而引发错误。
对于不能在类声明中初始化静态成员的一种例外情况是,静态成员为const整数或枚举型。
字符串单独保存在堆内存中,对象仅保存了指出到哪里去查找字符串的信息。
str = s;
这只保存了地址,而没有创建字符串副本,不能这样做。
程序清单12.3vegnews.cpp
//2023年2月26日22:21:22
//vegnews.cpp -- 使用new和delete
#pragma warning(disable:4996)
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using std::cout;
#include "strngbad.h"
void callme1(StringBad&);//传递引用
void callme2(StringBad);//传递值
int main()
{
using std::endl;
{
cout << "Starting an inner block.\n";
StringBad headline1("Celery Stalks at Midnight");
StringBad headline2("Lettuce Prey");
StringBad sports("Spinach Leaves Bowl for Dollars");
cout << "headline1: " << headline1 << endl;
cout << "headline2: " << headline2 << endl;
cout << "sports: " << sports << endl;
callme1(headline1);
cout << "headline1: " << headline1 << endl;
callme2(headline2);
cout << "headline2: " << headline2 << endl;
cout << "Initialize one object to another:\n";
StringBad sailor = sports;
cout << "sailor: " << sailor << endl;
cout << "Assign one object to another:\n";
StringBad knot;
knot = headline1;
cout << "kont:" << knot << endl;
cout << "Exiting the block.\n";
}
cout << "End of main()\n";
system("pause");
return EXIT_SUCCESS;
}
void callme1(StringBad& rsb)//传递引用
{
cout << "String passed by reference:\n";
cout << "\"" << rsb << "\"\n";
}
void callme2(StringBad sb)//传递值
{
cout << "String passed by value:\n";
cout << "\"" << sb << "\"\n";
}