类成员为指针时的有关的注意事项

       刚学习类与动态内存时,觉得书本上写的理解起来挺费劲的,现在我将自己学习这方面的知识时的有关内容总结如下(学习使用的书本为c++ primer plus,故下面的内容主要以自己理解书中的内容为主)。下面为项目的代码:

头文件:Stringbab

#include<iostream>
#ifndef _STRINGBAB_H_
#define _STRINGBAB_H_

class Stringbab {

private:
	char* str;
	int len;
	static int num_strings;
public:
	Stringbab(const char* s);
	Stringbab();
	~Stringbab();
	friend std::ostream& operator<<(std::ostream &os, const Stringbab& st);
};


#endif

类方法的定义:

#include<iostream>
#include"strngbab.h"

#include<string.h>

using std::cout;

int Stringbab::num_strings = 0;

Stringbab::Stringbab(const char* s) {

	len = std::strlen(s);
	str = new char[len + 1];
	strcpy_s(str,strlen(s)+1, s);
	num_strings++;
	cout << num_strings << ":\"" << str << "\"object created\n";

}
Stringbab::Stringbab() {

	len = 4;
	str = new char[4];
	strcpy_s(str,strlen("c++")+1, "c++");
	cout << num_strings << ":\"" << str << "\" default object created\n";
}
Stringbab::~Stringbab() {

	cout << "\"" << str << "\"object delete,";
	num_strings--;
	cout << num_strings << " left\n";
	delete[] str;
}
std::ostream & operator<<(std::ostream & os, const Stringbab& st) {

	os << st.str;
	return os;

 }

相关程序:

#include<iostream>
#include"strngbab.h"
using std::cout;
void callme1(Stringbab&);
void callme2(Stringbab);

int main() {

	using std::endl;
	{

		cout << "starting an inner block.\n";
		Stringbab headline1("Celery Staclks at Midnight");
		Stringbab headline2("Lettuce Prey");
		Stringbab sports("Spanich 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";
		Stringbab sailor = sports;
		cout << "sailor:" << sailor << endl;
		cout << "Assign one  object to another:\n";
		Stringbab knot;
		knot = headline1;
		cout << "knot:" << knot << endl;
		cout << "Exiting the block!\n";

	}
	cout << "End of main()";


	return 0;
}
void callme1(Stringbab& rsb) {

	cout << "String passed by referance:\n";
	cout << "\" " << rsb << "\"";
}
void callme2(Stringbab sb) {

	cout << "String passed by value:\n";
	cout << "\" " << sb << "\"";
}

下面是程序的运行结果:

(注:这里的运行结果为vs2019上运行的结果,与书本的不太一样,书本上的运行结果显示表明析构函数调用的次数比这的结果多两次,原因可能是vs2019并没有使程序运行完就中断了)

这里的运行结果中,显示headline2对象时出现了乱码原因如下:

程序在调用callme2函数时,使用的是值传递,将headline2对象的值复制给一个临时Stringbab对象,这里的赋值为成员赋制或浅复制,分别将Headline2对象的各个成员数据复制给临时对象对应的成员,故Headline2对象的指针成员复制给临时对象对应的指针成员,所以两个对象指向相同的地址。当callline2函数调用结束时,临时对象的析构函数将释放该对象的内存,由于这两个对象的指针成员指向相同的内存,故临时对象清除该成员指针指向的内存时,也将Headline2的指针成员指向的内存清除。当显示Headline2.str时,由于指针指向的内存被清除,故显示出一堆乱码。

要解决这个问题,应该定义多一个Stringbab类的复制函数(称为深度复制),让指针指向内容的副本地址复制给临时对象对应的指针成员。该复制函数的定义如下:

Stringbab::Stringbab(const Stringbab& st) {

	num_strings++;
	len = st.len;
	str = new char[len + 1];
	strcpy_s(str, len + 1, st.str);
	cout << num_strings << ":\"" << str << "\"object created\n";
}

加入该段代码后,程序的运行结果如下:

 

 

 

结果仍然有乱码,显示最后一个对象Headline1的成员指针指向的内存的内容时出现了乱码,可能是该内存已被删除。检查程序时,发现有一个语句较为可疑:knot=headline1,因为这两个对象的内容是一样的,可能是knot对象的析构函数同时将headline1指针成员指向的内容也清除了,该问题就跟上面的浅复制的问题一样。我们没有定义过赋值函数,故在执行该赋值语句时,调用的是默认赋值函数,该函数用浅复制的方法,赋值时,将headline1的内容复制给knot对象,使knot.str指向的内存地址跟headline1.str指向的内存地址一样。为了解决这个问题,我们应该定义一个赋值函数,赋值时,将headline1对象复制给knot对象时,用的应该是深复制。该函数的定义如下:

Stringbab& Stringbab::operator = (const Stringbab& 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;
}

加入该函数后,程序的运行结果如下:


 

运行结果是没毛病的,所以我们之前对最后出现乱码的猜测是正确的。

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值