1.聚合/组合关系在构建时的问题
class Point
{
private:
int p_x;
int p_y;
public:
Point(int x = 0, int y = 0) :p_x(x), p_y(y)
{
cout << "Point Create " << this << endl;
}
Point(const Point& p) :p_x(p.p_x), p_y(p.p_y)
{
cout << "Point Copy " << this << endl;
}
~Point()
{
cout << "Point Destroy " << this << endl;
}
};
class Circle
{
private:
Point c_p;
int c_r;
public:
Circle(int r = 0) :c_r(r)
{
cout << "缺省 Circle Create " << this << endl;
}
Circle(const Point& c, int r) : c_p(c), c_r(r)
{
cout << "二参 Circle Create " << this << endl;
}
Circle(int x , int y, int r) :c_p(x, y), c_r(r)
{
cout << "三参 Circle Create " << this << endl;
}
~Circle()
{
cout << "Circle Destroy " << this << endl;
}
};
1、默认调用缺省构造函数
int main()
{
Circle c1;//给c1分配12个字节,然后调用Circle的构造函数,在函数中创建成员属性,先调用缺省构造函数创建c_p对象,然后再创建c_r对象
//在没有写组合对象的构造函数,系统会默认加入其缺省构造函数,若没有缺省构造函数,则不会编译通过
//在析构时是相反的,先析构圆c1,再析构点c_p
return 0;
}
2、构造函数的优化(尽可能设计为引用)
int main()
{
Point p(30,30);
Circle c2(p,8);//两参构造函数的第一个参数设计为引用的目的是为了减少构建和析构对象,若设计为普通对象,则在调用该函数时会多进行一次拷贝构造和析构
return 0;
}
设计为普通对象时:
设计为引用时:
明显少一次点对象的拷贝构造和析构
3、圆的拷贝构造和赋值需要明确调用点的拷贝构造和赋值
int main()
{
Circle c1(2,4,6);
Circle c2(c1);
return 0;
}
//此时没有圆的拷贝构造只有点的拷贝构造
结果:
调用了编译器给的圆的拷贝构造函数,该函数中编译器加入了点的拷贝构造。
//加上圆类的无动作的拷贝构造
Circle(const Circle& c)
{
cout << "Copy Create " << this << endl;
}
int main()
{
Circle c1(2,4,6);
Circle c2(c1);
return 0;
}
结果:
且此时c2的值不等于c1。
原因是:在调用圆的拷贝构造时,由于没有明确给出点的拷贝构造,此时为了构建c2圆对象中的c_p点对象,就会调用缺省构造函数,所以c_p的值为0,0,而半径未构建则为随机值。
总结:整体类的拷贝构造函数中一定要明确调用部分类的拷贝构造,否则编译器会部分类的默认将缺省构造函数加入到整体类的拷贝构造函数中。如下:
Circle(const Circle& c):c_p(c.c_p),c_r(c.c_r)
{
cout << "Copy Create " << this << endl;
}
赋值运算符重载函数同样存在上述类似情况:在整体类的赋值函数中要明确调用部分类的赋值函数,否则部分类的对象将不会被赋值,依然为原来的值。
4、初始化列表和函数体
Circle(const Circle& c):c_r(c.c_r)
{
c_p=c.c_p;
cout << "Copy Create " << this << endl;
}
初始化列表则是调用点的拷贝构造来进行,而函数体的这种方式只是赋值,并且在赋值前还有多一步调用缺省构造函数的过程。
对于内置类型使用初始化列表和函数体的效率没有太大区别,但对于设计的类类型,前者只调用一次拷贝构造,而后者要先调用缺省构造函数,再调用一次赋值运算符重载函数。
//加上点的赋值函数
Point& operator=(const Point& p)
{
if (this != &p)
{
p_x = p.p_x;
p_y = p.p_y;
}
cout << "operator=" << this << endl;
return *this;
}
初始化列表方式
函数体方式:
很明显直接使用初始化列表方式调用的函数更少。
总结:为了使对象的方法尽可能少的被调动,尽量在初始化列表中给出初始化过程,而不要在函数体中进行。
2.定义自身的问题
class Bar
{
private:
std::string _name;
Bar sm_bar;//error
static Bar sm_bar;//ok
public:
Bar(const string& name="#"):_name(name)
{}
};
Bar Bar::sm_bar("13");//ok
int main()
{
Bar bar("123");
return 0;
}
不能在类中定义类自身,否则会造成无穷递归:
也叫做不完整类型
而可以在类中定义静态类自身,因为静态变量只有一份,不会形成递归;也可以在类中定义类自身指针,因为在创建对象时,不需要使用对象实体给指针进行赋值,没有实体对象就不会进行无限递归;但是在类中不能定义类自身引用,因为与指针相反,创建对象时,必须使用对象实体对引用进行赋值,因为没有空引用,而对象还未创建无法进行赋值。
综上,只要存在对象实体,就会有对象的对象需要创建,依次递归,造成无限递归。
3.实例之文件读写加密解密
class Readfile
{
public:
void Read(const string& filename,string& str)const
{
ifstream ifpr;
ifpr.open(filename);
if (!ifpr.is_open())
{
cout << "file open err" << endl;
return;
}
//获取文件大小的方法:
//最优方法
struct stat stbuff;
stat(filename.c_str(), &stbuff);
size_t filesize = stbuff.st_size;
str.reserve(filesize);
//其次方法
/*ifpr.seekg(0, ios_base::end);
streampos fs = ifpr.tellg();
ifpr.seekg(0, ios_base::beg);*/
char ch;
while (ifpr.peek() != EOF)//不使用(!ifpr.eof()),因为该方法会重复读取最后一个字符
{
ifpr.get(ch);
str.push_back(ch);
}
ifpr.close();
}
};
class Writefile
{
public:
void Write(const string& namefile,const string& str)const
{
ofstream ofpr;
ofpr.open(namefile);
if (!ofpr.is_open())
{
cout << "file open err" << endl;
return;
}
for (auto& x : str)
{
ofpr.put(x);
}
ofpr.close();
}
};
class Encrptr//加密
{
public:
//返回新密文字符串
string Encrypt1(const string& str,const int m = 5)const
{
cout << "原明文:" << endl;
cout << str << endl;
cout << "数据加密,将明文转成密文:" << endl;
string es;
es.reserve(str.size() + 1);
for (auto& x : str)
{
char ch = x;
//如果为字母(要区分大小写字母),加密为其后第m位字母。如:m为5,b加密为g,x加密为c
//如果为数字,加密为其加上m-1。如:m为5,5加密为9,8加密为3
//其他字符不加密,注意,存在汉字时,一个汉字是由两字节表示,其高位为1,值为负数,
//而字符串函数只能处理0~127的值
if (ch > 0)
{
if (islower(ch))//小写字母
{
ch = ch - 'a';
ch = (ch + m) % 26 + 'a';
}
else if (isupper(ch))//大写字母
{
ch = ch - 'A';
ch = (ch + m) % 26 + 'A';
}
else if (isalnum(ch))//数字
{
ch = ch - '0';
ch = (ch + m - 1) % 10 + '0';
}
}
es.push_back(ch);
}
cout << es << endl;
return es;
}
//将str直接从明文改成密文
void Encrypt2(string& str, const int m = 5)const
{
cout << "原明文:" << endl;
cout << str << endl;
cout << "数据加密,将明文转成密文:" << endl;
for (auto& x : str)
{
//如果为字母(要区分大小写字母),加密为其后第m位字母。如:m为5,b加密为g,x加密为c
//如果为数字,加密为其加上m-1。如:m为5,5加密为9,8加密为3
//其他字符不加密,注意,存在汉字时,一个汉字是由两字节表示,其高位为1,值为负数,
//而字符串函数只能处理0~127的值
if (x > 0)
{
if (islower(x))//小写字母
{
x = x - 'a';
x = (x + m) % 26 + 'a';
}
else if (isupper(x))//大写字母
{
x = x - 'A';
x = (x + m) % 26 + 'A';
}
else if (isalnum(x))//数字
{
x = x - '0';
x = (x + m - 1) % 10 + '0';
}
}
}
cout << str << endl;
}
};
class Decryptr//解密
{
public:
//返回新明文字符串
string Decrypt1(const string& str, const int n = 5)const
{
cout << "原密文:" << endl;
cout << str << endl;
cout << "数据解密,将密文转成明文:" << endl;
string ds;
ds.reserve(str.size() + 1);
for (auto& x : str)
{
char ch = x;
if (ch > 0)
{
if (islower(x))//小写字母
{
ch = ch - 'a';
ch = (ch - n) % 26 + 'a';
}
else if (isupper(x))//大写字母
{
ch = ch - 'A';
ch = (ch - n) % 26 + 'A';
}
else if (isalnum(x))//数字
{
ch = ch - '0';
ch = (ch - n + 1) % 10 + '0';
}
}
ds.push_back(ch);
}
cout << ds << endl;
return ds;
}
//将str直接从密文改成明文
void Decrypt2(string& str, const int n = 5)const
{
cout << "原密文:" << endl;
cout << str << endl;
cout << "数据解密,将密文转成明文:" << endl;
for (auto& x : str)
{
if (x > 0)
{
if (islower(x))//小写字母
{
x = x - 'a';
x = (x - n) % 26 + 'a';
}
else if (isupper(x))//大写字母
{
x = x - 'A';
x = (x - n) % 26 + 'A';
}
else if (isalnum(x))//数字
{
x = x - '0';
x = (x - n + 1) % 10 + '0';
}
}
}
cout << str << endl;
}
};
//加密器,由读写加密聚合而成
class EncryptFacade
{
private:
Readfile rfile;
Writefile wfile;
Encrptr eptr;
public:
EncryptFacade() {}
~EncryptFacade() {}
void FileEncryptFacade(const string rfilename, const string wfilename)
{
string tmpstr;
/*rfile.Read(rfilename,tmpstr);
tmpstr=eptr.Encrypt1(tmpstr, 5);
wfile.Write(wfilename, tmpstr);*/
rfile.Read(rfilename, tmpstr);
eptr.Encrypt2(tmpstr, 5);
wfile.Write(wfilename, tmpstr);
}
};
//解密器,由读写解密聚合而成
class DecryptFacade
{
private:
Readfile rfile;
Writefile wfile;
Decryptr dptr;
public:
DecryptFacade() {}
~DecryptFacade() {}
void FileDecryptFacade(const string& rfilename, const string& wfilename)
{
string tmpstr;
/*rfile.Read(rfilename,tmpstr);
tmpstr=dptr.Decrypt1(tmpstr, 5);
wfile.Write(wfilename, tmpstr);*/
rfile.Read(rfilename, tmpstr);
dptr.Decrypt2(tmpstr, 5);
wfile.Write(wfilename, tmpstr);
}
};
int main()
{
EncryptFacade ef;
ef.FileEncryptFacade("student.h","w.txt");
DecryptFacade df;
df.FileDecryptFacade("w.txt", "r.txt");
return 0;
}