不好的类
该例子基于《C++ primer plus 第6版》第12章1节
从一个例子看类中的隐式构造函数
"stringbad.h"文件
#include <iostream>
#ifndef STRINGBAD_H
#define STRINGBAD_H
class StringBad
{
private:
char* str;
int len;
static int num_strings;
public:
StringBad();
StringBad(const char* s);
~StringBad();
// friend function
friend std::ostream & operator<<(std::ostream& os,const StringBad& st);//重载<<
};
#endif
"stringbad.cpp"文件
#include <cstring>
#include "stringbad.h"
#include <windows.h>
// 知识点1
int StringBad::num_strings = 0;
using std::cout;
using std::endl;
StringBad::StringBad()
{
len = 4;
str = new char[4];
std::strcpy(str, "C++");
num_strings++;
cout << num_strings << ": " << str << " create";
}
StringBad::StringBad(const char *s)
{
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
num_strings++;
cout << num_strings << ":" << str << " create" << endl;
}
StringBad::~StringBad()
{
cout << str << " deleted" << endl;
--num_strings;
cout << num_strings << " string left" << endl;
delete[] str;
}
// friend function
std::ostream &operator<<(std::ostream &os, const StringBad &st)
{
os << st.str << std::endl;
}
"main.cpp"
void callByRef(StringBad& str);
void callByValue(StringBad str);
int main(void)
{
{
StringBad str1("str1");
StringBad str2("str2");
StringBad str3("str3");
callByRef(str1);
cout << "str1:" << str1 << endl;
callByValue(str2);//知识点2
cout << "str2:" << str2 << endl;
StringBad otherStr = str3;//知识点2
cout << "otherStr:" << otherStr << endl;
StringBad otherStr2;
otherStr2 = str1;//知识点3
cout << "otherStr2" << otherStr2 << endl;
cout<<"exit the block."<<endl;
}
cout<<"end of main"<<endl;
system("pause");
return 0;
}
运行结果:(可能每次都不一样)
1:str1 create
2:str2 create
3:str3 create
by reference :str1
str1:str1
by value :str2
str2 deleted
2 string left
str2:€q //发生了异常了
otherStr:str3
3: C++ createotherStr2str1
exit the block.
str1 deleted
2 string left
str3 deleted
1 string left
q铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪铪3私?a //哈哈哈
知识点1–类静态变量的初始化
- 初始化类静态变量,所有的类共用一个,和其他普通成员变量分开存,不能在声明的时候进行初始化静态成员变量(例如创建对象?),因为声明的时候只是描述了如何分配内存,但并不分配内存,但是如果静态成员是整型或者枚举型const,可以在声明的时候进行初始化
知识点2–隐式复制构造函数
发生的情况
- 在函数中按值传递,例如以上代码中的callByValue(str2);
- 在对象声明中直接赋值,例如 StringBad otherStr = str3;
隐式复制构造函数的结构
其实就是编译器在你没有进行显式声明复制构造函数的情况下,自行定义了一个**Class_name(const Class_name&)**的构造函数
实现的功能
- 对普通成员变量,进行浅复制,也就是只是将对象的指针指向了同一块内存区域。所以,在上述代码中callByValue(str2),将str2浅复制给了一个临时变量,在离开callByValue方法区域的时候,该临时变量调用析构函数,释放了str2.str的内存,所以str2.str指向了一个不确定的内存区域,再次调用的时候会乱码
- 对于类静态变量,不进行复制
解决的办法
自定义一个显式复制构造函数,对用new申请的内存区域进行深复制。
知识点3–赋值运算符
发生的情况
将一个对象赋值给另一个已经初始化过的对象,例如
StringBad otherStr2;
otherStr2 = str1;//知识点3
结构
其实就是编译器在没有进行显式重载=运算符的情况下,自行定义了一个**Class_name& Class_name::operator=(const Class_name &)**的重载函数
功能
- 将=右边的对象的引用赋值给了左边,类似于复制构造函数,隐式实现了对普通成员进行逐个复制,静态数据成员不受影响。
- 可能会导致成员变量new的内存区域多次被释放
解决办法
仍然是显式实现,深复制
改进后的类
- 新增自定义显式复制构造函数
- 新增自定义显式=重载函数
StringBad::StringBad(const StringBad &st)
{
num_strings++;
len = st.len;
str = new char[len + 1];
std::strcpy(str, st.str);
cout << num_strings << ": " << str << " created." << endl;
}
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;
}