《C++PrimerPlus》第12章 类和动态内存分配

发布时间:2023年12月23日

12.1 动态内存和类

12.2 改进后的新String类

动态内存和类示例(编写一个String类,实现字符串的操作)

头文件string1.h

#ifndef __STRING__H__
#define __STRING__H__
#include <iostream>

using namespace std;

class String {
private:
	char *str; // 字符串的地址(字符指针)
	int len; // 字符串长度
	static int num_strings; // 创建的字符串的数量(静态变量)
	static const int CINLIM = 80; // 最多输入n个字符
public:
	String(const char *s); // 构造函数
	String(); // 默认构造函数
	String(const String &st); // 复制构造函数
	~String(); // 析构函数
	int length() const { return len; } // 返回字符串长度

	String &operator=(const String &st); // 重载=运算符
	String &operator=(const char *s); // 重载=运算符
	char &operator[](int i); // 重载[]运算符
	const char &operator[](int i) const; // 重载[]运算符

	friend bool operator<(const String &str1, const String &str2); // 重载<运算符
	friend bool operator>(const String &str1, const String &str2); // 重载>运算符
	friend bool operator==(const String &str1, const String &str2); // 重载==运算符
	friend ostream &operator<<(ostream &os, const String &st); // 运算符<<重载
	friend istream &operator>>(istream &is, String &st); // 运算符<<重载

	static int Howmany(); // 返回总共创建了几个字符串
};

#endif

源代码main.cpp

#include "string1.h"
using namespace std;
const int MaxLen = 81;

int main() {
	cout << "Hi, What's your name?" << endl;
	String name;
	cin >> name;
	cout << name << ", please enter a string: " << endl;
	String saystrings;
	char temp[MaxLen];

	cin.get(temp, MaxLen);
	while (cin && cin.get() != '\n') continue;
	
	saystrings = temp;
	cout << "Here is your sayings: " << saystrings << endl;

	String str = "Hello dad";
	if (saystrings.length() < str.length())
		cout << "str is longer." << endl;
	else
		cout << "saystring is longer." << endl;
	if (saystrings < str)
		cout << "saystrings's first letter is smaller." << endl;
	else
		cout << "str's first letter is smaller." << endl;

	cout << "This program used " << String::Howmany() << " objects." << endl;
	return 0;
}

源代码string1.cpp

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

// 注意不能在类声明中初始化静态成员变量
// 声明只是描述如何分配,并不分配内存
int String::num_strings = 0;

int String::Howmany() {
	return num_strings;
}

String::String(const char *s) { // StringBad str("Hello")
	len = strlen(s); // 获取字符串长度
	str = new char[len + 1]; // 开辟内存空间
	strcpy_s(str, len + 1, s); // 把s拷贝到str
	num_strings++;
}

String::String() {
	len = 0;
	str = new char[1];
	str[0] = '\0';
	num_strings++;
}

String::String(const String &st) {
	len = st.len;
	str = new char[len + 1];
	strcpy_s(str, len + 1, st.str);
	num_strings++;
}

String::~String() {
	num_strings--;
	delete[] str;
}

String & String::operator=(const String &st) {
	if (this == &st) return *this; // 避免自己赋值自己(不可以有删除操作)
	delete[] str; // 删掉旧的数据
	len = st.len;
	str = new char[len + 1];
	strcpy_s(str, len + 1, st.str);
	return *this; // this是指针,需要取值(值为对象的引用)
}

String & String::operator=(const char *s) {
	delete[] str;
	len = strlen(s);
	str = new char[len + 1];
	strcpy_s(str, len + 1, s);
	return *this;
}

char & String::operator[](int i) {
	return str[i];
}

const char & String::operator[](int i) const {
	return str[i];
}

bool operator<(const String &str1, const String &str2) {
	return (strcmp(str1.str, str2.str) < 0);
}

bool operator>(const String &str1, const String &str2) {
	return str2 < str1;
}

bool operator==(const String &str1, const String &str2) {
	return (strcmp(str1.str, str2.str) == 0);
}

ostream &operator<<(ostream &os, const String &st) {
	os << st.str;
	return os;
}

istream &operator>>(istream &is, String &st) {
	char temp[String::CINLIM];
	is.get(temp, String::CINLIM);
	if (is) st = temp;
	while (is && is.get() != '\n') continue;
	return is;
}

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

12.4 有关返回对象的说明

12.5 使用指向对象的指针

使用new运算符示例(注意创建的地址偏移和析构函数的调用)

#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; // 定位new运算符,没有开辟空间,定位到之前开辟过的空间
	pc2 = new JustTesting("Heap1", 20); // 开辟了新的空间

	cout << "buffer: " << (void *)buffer << endl; // 打印地址
	cout << "heap: " << pc2 << endl;

	cout << pc1 << ": "; // 打印地址
	pc1->Show(); // 打印值
	cout << pc2 << ": ";
	pc2->Show();

	JustTesting *pc3, *pc4;
	// pc3 = new(buffer) JustTesting("Bad Idea", 6); // 极其不推荐这么做,存到同一个地方可能会数据冲突
	pc3 = new(buffer + sizeof(JustTesting)) JustTesting("Better Idea", 6);
	pc4 = new JustTesting("Heap2", 10);

	cout << pc3 << ": ";
	pc3->Show();
	cout << pc4 << ": ";
	pc4->Show();

	delete pc2; // 仅当显式调用delete的时候,析构函数被调用
	delete pc4;

	pc1->~JustTesting(); // 显式调用析构函数
	pc3->~JustTesting();

	delete[] buffer;
	return 0;
}

12.6 复习各种技术

12.7 队列模拟

设计一个队列类

头文件queue1.h

#ifndef __QUEUE_H__
#define __QUEUE_H__
#include <iostream>
using namespace std;

class Customer { // 客户信息
private:
	long arrive; // 开始操作的时间
	int processtime; // 操作的总时间
public:
	Customer() {
		arrive = processtime = 0;
	} // 都初始化为0
	void set(long when); // 设置开始操作的时间
	long when() const { return arrive; } // 返回开始操作的时间
	int ptime() const { return processtime; } // 返回操作总时间
};

typedef Customer Item; // Item是Customer类的对象

class Queue {
private:
	enum { Q_SIZE = 10 };
	struct Node {
		Item item; // 存放的信息(Item表示任意类型)
		struct Node *next; // 指向下一个结构体的指针
	};
	Node *front; // 指向队列头的指针
	Node *rear; // 指向队列尾的指针
	int items; // 当前队列的长度(当前有多少人)
	const int qsize; // 队列最大值
public:
	Queue(int qs = Q_SIZE); // 构造函数
	~Queue(); // 析构函数
	bool isempty() const; // 判断队列是否为空
	bool isfull() const; // 判断队列是否为满
	int queuecount() const; // 返回队列长度
	bool enqueue(const Item &item); // 入队(返回bool表示是否成功)
	bool dequeue(Item &item); // 出队(返回bool表示是否成功),值存在形参中
};

#endif // !__QURUE_H__

源代码main.cpp

#include "queue1.h"

int main() {
	int qs; // 队列长度
	Item temp; // 创建一个Customer对象
	int i = 0;
	int customers = 0; // 有几个顾客

	cout << "Enter maxium size of queue: ";
	cin >> qs;

	Queue line(qs); // 创建队列
	while (!line.isfull()) { // 入队,直到队列为满
		temp.set(i++); // 填充temp的值
		line.enqueue(temp);
		customers++;
	}
	cout << "Customers: " << customers << endl;

	while (!line.isempty()) { // 出队,直到队列为空
		line.dequeue(temp);
		customers--;
	}
	cout << "Now, customers: " << customers << endl;

	return 0;
}

源代码queue1.cpp

#include "queue1.h"
#include <cstdlib>

Queue::Queue(int qs) : qsize(qs) {
	front = rear = nullptr;
	items = 0;
	// qsize = qs; // 错误!qsize是常量,可以初始化,不可以赋值
	// 成员初始化列表(member Initializer list)
	// 只能用于构造函数,位于参数列表的右括号之后,函数体左括号之前
}

Queue::~Queue() {
	Node *temp;
	while (front != nullptr) {
		temp = front;
		front = front->next;
		delete temp;
	}
}

bool Queue::isempty() const {
	return items == 0;
}

bool Queue::isfull() const {
	return items == qsize;
}

int Queue::queuecount() const {
	return items;
}

bool Queue::enqueue(const Item &item) {
	if (isfull()) return false; // 满队列直接返回false
	Node *add = new Node; // 开辟新的内存空间来存储新节点
	add->item = item; // 值就是传递进来的参数
	add->next = nullptr; // 因为是末尾节点,所以指向空
	items++; // 队列数量+1

	if (front == nullptr) front = add; // 如果为空队列,则当前节点变为头节点
	else rear->next = add; // 否则,插入前的尾节点指向当前节点
	rear = add; // 当前节点变为尾节点
	return true;
}

bool Queue::dequeue(Item &item) {
	if (isempty()) return false; // 空队列直接返回false
	item = front->item; // 把头结点的值赋给形参
	items--; // 队列数量-1

	Node *temp = front; // 使用临时指针存一下头结点的值
	front = front->next; // 调整头结点为下一个值
	delete temp; // 释放临时指针所指向的内存空间
	if (items == 0) rear = nullptr; // 如果为空队列,则尾节点变为空
	return true;
}

void Customer::set(long when) {
	arrive = when; // 记录开始操作的时间
	processtime = rand() % 3 + 1; // 随机出一个1~3分钟的总操作时长
}

文章来源:https://blog.csdn.net/Mako5455/article/details/134813806
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。