设计一个字符串类,下面的代码是一个不好的设计,起名StringBad。
//stringbad.h
#pragma once
//一个设计有问题的string类
#include <iostream>
using namespace std;
class StringBad
{
public:
StringBad();//默认构造函数
StringBad(const char* s);//构造函数
~StringBad();//析构函数
friend ostream& operator<<(ostream& os, const StringBad& s);//友元函数,输出函数
private:
char* str;//指向保存的字符串
int len;//字符串的长度
static int num_strings;//创建的对象数量
};
//stringbad.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "stringbad.h"
#include <cstring>
int StringBad::num_strings = 0;//类中的变量
StringBad::StringBad()//默认构造函数
{
len = strlen("趣字节");
str = new char[len + 1];//动态创建内存,用于存放默认字符串
strcpy(str,"趣字节");
num_strings++;//对象数量+1
cout << "默认构造函数," << str << ",对象数量:" << num_strings << endl;//不重要的输出
}
StringBad::StringBad(const char* s)//构造函数
{
len = strlen(s);
str = new char[len + 1]; //动态创建内存,用于存放传入的字符串s
strcpy(str,s);
num_strings++;//对象数量+1
cout << "构造函数," << str << ",对象数量:" << num_strings << endl;//不重要的输出
}
StringBad::~StringBad()
{
cout << "析构函数," << str; //不重要的输出
delete[]str; //释放动态内存
str = NULL;
len = 0;
num_strings--;//对象数量-1
cout << ",对象数量:" << num_strings << endl;//不重要的输出
}
ostream& operator<<(ostream& os, const StringBad& s)//友元函数,输出函数
{
os << s.str << endl;
return os;
}
测试程序如下:
#include"stringbad.h"
int main()
{
StringBad s1("趣字节,有趣的编程!!!");
cout << "s1:" << s1;
cout << "-------------" << endl;
StringBad s2 = s1;
cout << "s2:" << s1;
cout << "-------------" << endl;
StringBad s3;
s3 = s1;
cout << "s3:" << s3;
return 0;
}
程序运行崩溃,并提示错误
上面的代码在构造s2时已经出错(s3也有问题),构造s2时并没有调用构造函数,后面在析构s2时程序直接崩溃。这就是常说的浅拷贝导致的问题。
C++提供下面这些默认函数(如果您没有提供):
●默认构造函数。不接受参数也不执行任何操作。
●默认析构函数,不执行任何操作。
●拷贝(复制)构造函数。用对象初始化另一个新建对象,逐个复制非静态成员,复制的是成员的值(浅复制)。
●拷贝赋值运算符。用对象赋值给另一个对象,逐个复制非静态成员,复制的是成员的值(浅复制)。
●移动构造函数。C++11增加。
●移动赋值运算符。C++11增加。
●地址运算符。返回对象的地址。和我们想象一致,不再讨论。
使用默认拷贝构造函数和使用默认=赋值运算符,导致浅拷贝,在析构时会出现重复释放(delete)同一段内存,导致程序崩溃。
解决的办法:自己定义拷贝构造函数和赋值=运算符重载函数,实现深拷贝。