C++ Primer Plus 12章(一)

C++ Primer Plus 12章(一)

早上阅读了一个小时,思考了良久,还是决定开始写下一些见解来记录自己所得。同时也给别人一些借鉴,也能得到反馈。

之前的11章,我会慢慢的补充上去。

在讲内容之前,我想先谈论一下这本书。从我真正静下心来每天花一个小时以上的时间去阅读后,才逐渐明白书里讲的内容。关于这本C++的书,有些章节写的极好,但有些章节我觉得还差了一点详细的东西,可能不太适合初学者,至少对C++和C有了一定了解之后,看这本书是很好的。

下面,进入正题:

(1)类中的成员使用指针和new进行动态内存分配

好处:假设我们要定义一个people类,每个people类里面有人名这个成员,我们使用字符数组类型来存储名字,但是我们究竟要使用多大的字符数组空间呢?用5个会不会小了,用20个又会不会多了?如果当我们创建的对象超过成千上万时,那么将会对内存空间造成极大的浪费。因此,此时使用new运算符来动态的为名称指定空间大小是最合适的,并且它会在对象结束的时候使用delete释放它。

坏处:这里我使用书上一个实际的例子进行示范,坏处在哪里。

定义一个stringBad 类:

class StringBad
{
	private:
		char *str;//字符指针
		int len;
		static int num_strings;//每创建一个对象时,此数值加1,因为使用了static修饰,无论创建了多少对象,该成员被所有对象共享,只创建一个副本
	public:
		StringBad(const char *s);//自定义构造函数
		StringBad();//默认构造函数
		~StringBad();析构函数
}

然后是它对应成员函数的实现方法:

StringBad::StringBad(const char *s)
{
	len = std::strlen(s);//注意strlen函数获取字符串长度时,不包括最后的“\0”字符
	str = new char[len+1];
	std::strcpy(str,s);
	num_strings++;
}

StringBad::StringBad()
{
	len = 4
	str = new char[4];
	std::strcpy(str,"C++");
	num_strings++;
}

StringBad::~StringBad()
{
	--num_strings;
    delete []str;
}
最后是在main中创建对象,并初始化它,这也是问题所在,请注意:
void callme1(StringBad &);
void callme2(StringBad);

int main()
{
    using std::endl;
    StringBad headline1("a");
    StringBad headline2("b");
    StringBad sports("c");
    
    callme1(headline1);
    callme2(headline2);//这里的调用是关键
    
    StringBad sailor = sports;//这里和下面的两种对象初始化,都是存在问题的
    
    StringBad knot;
    knot = headline1;
    
    return 0;
}

void callme1(StringBad & rsb)
{
	cout<<rsb<<endl;   
}

void callme2(StringBad sb)
{
    cout<<sb<<endl;
}

分析:

在主函数中,创建如下两个对象并赋值的时候,其实可以知道,这两种初始化方法既没有调用默认的构造函数,也没有调用自定义的构造函数,而是使用了一种新的函数:复制构造函数(浅复制)。

StringBad sailor = sports;//这里和下面的两种对象初始化,都是存在问题的

StringBad knot;
knot = headline1;

这里导致的结果就是,本来sports对象创建时,会调用自定义的构造函数,生成一个指针,指向字符串str所在的内存地址;但是现在将sports赋值给sailor对象时,sailor对象也会生成一个指针指向字符串str所在的内存地址;即这两个对象指向了同一个内存地址。

那么在程序结束后调研析构函数进行内存释放时,先释放sailor是可以正常释放的,但是再去释放sports时,就会出错,因为sports指向的内存地址已经被释放了。

同理,创建knot时,释放knot时,也会出现类似的错误。

这就是类中指针成员的坏处。

解决方法:显示的定义一个复制构造函数(深复制)

StringBad::StringBad(const StringBad &st)
{
	num_strings++;//每创建一个对象时,此数值加一
	len = st.len;
	str = new char[len+1];
	std::strcpy(str,st.str);
}

在这里面,我们重新使用new分配了内存空间,并有一个新的指针指向新的内存地址,但是内存地址里面存储的内容是一样的。

以此,可以解决指针成员初始化带来的问题。

(2)赋值运算符

上述问题,并不仅仅是因为复制构造函数和指针造成的,还有一些其他问题。比如赋值!

StringBad sailor = sports;//这里和下面的两种对象初始化,都是存在问题的

StringBad knot;
knot = headline1;

C++中,是允许可以直接将同类的对象进行直接赋值初始化的,但是它同样会带来和复制构造函数一样的问题,两个对象的指针指向同一个内存地址,释放一个之后,另一个就没了,不能正常释放。

解决方法:编写赋值运算符

相当于对 “=”运算符进行重载,只能由类成员函数来完成。

StringBad & StringBad::operator=(const StringBad &st)
{
    if(this == &st)
        return *this;
    delete []str;
	len = st.len;
	str = new char[len+1];
	std::strcpy(str,st.str);
    return *this;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值