1.静态类成员:
private:
char * str;
int length;
static int objectNum; //静态数据成员,为所有的对象所共享
上例中的objectNum就是静态类成员,它是所有对象所共享的,如下图:
在上例中,创建了三个对象,内存会给每个对象都分配数据单元用来存储Str和Len,但是不会为每个对象都创建num_strings,在内存中只会创建一个num_strings,供所有对象共享。静态类数据成员的初始化不是在类的声明文件中进行的,而是在类的函数定义文件中进行的。如在StringBad.cpp文件中初始化,而不是在StringBad.h文件中初始化。
2.特殊成员函数:
●默认构造函数:如果没有定义构造函数,就会自动生成。
●默认析构函数:如果没有定义析构函数,就会自动生成。
●复制构造函数:当用一个对象初始化另外一个对象时,如果我们没有专门定义复制构造函数,则会自动生成复制构造函数,详解见下面。
●赋值运算符:当用一个对象初始化另外一个对象时,如果我们没有专门定义赋值运算符,则会自动生成赋值运算符,详解见下面。
●地址运算符
3.复制构造函数:
class StringBad
{
private:
char * str;
int length;
static int objectNum; //静态数据成员,为所有的对象所共享
public:
StringBad(); //默认构造函数
StringBad(const char * s); //构造函数
~StringBad(); //析构函数
friend ostream & operator<<(ostream & os, const StringBad & sb);
};
int StringBad::objectNum = 0; //静态数据成员
StringBad::StringBad() //默认构造函数
{
length = 4;
str = new char[4];
strcpy(str, "c++");
objectNum++;
cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
StringBad::StringBad(const char * s) //构造函数
{
length = strlen(s);
str = new char[length + 1];
strcpy(str, s);
objectNum++;
cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
StringBad::~StringBad() //析构函数
{
objectNum--;
cout << str << " is deleted." << endl;
delete [] str;
cout << objectNum << " objects left." << endl;
}
ostream & operator<<(ostream & os, const StringBad & sb) //重载<<运算符
{
os << sb.str << endl;
return os;
}
int main()
{
StringBad sb1("abcde"), sb2 = sb1;
cout << sb1;
cout << sb2;
cout << endl;
return 0;
}
在上例中,定义了一个类StringBad,在类中定义了一些成员函数,然后使用sb2 = sb1,用sb1来初始化sb2,当用一个对象初始化另外一个对象时,就会调用复制构造函数,此时就调用了复制构造函数。
默认的复制构造函数逐个复制非静态成员(成员复制也成为浅拷贝),复制的是成员的值。如在上例中,其实是将sb1.length赋给sb2.length,sb1.str赋给sb2.str,而对象的str成员存储的是字符串的地址,而不是字符串本身,所以这种浅拷贝是将sb1.str和sb2.str指向同一个地方,所以这种浅拷贝容易出问题,当将sb1.str指向的内容删除时,其实也将sb2.str指向的内容也删除了,再删除sb2.str所指向的内容时就会出错。
所以,此时我们就需要自己定义一个复制构造函数来解决这种问题,将sb1.str和sb2.str指向不同的地方,但这两个地方存储的字符串内容是一样的(也就是深拷贝)。
浅拷贝和深拷贝的区别:浅拷贝是仅仅只拷贝字符串的地址,使两个指针指向同一个地方。深拷贝是拷贝内容到另外一个地方,使另一个指针指向拷贝内容的地方,通过深拷贝两个指针指向的地址就不同,但字符串内容仍是相同的。
浅拷贝示意图:
深拷贝示意图:
此例中我们自己定义一个复制构造函数,使得通过对象初始化,也能使两个对象的str指针指向不同的地址:
StringBad::StringBad(const StringBad & st) //复制构造函数,用于将一个对象用于初始化另外一个对象,如object1 = object2
{
objectNum++;
length = st.length;
str = new char[length + 1];
strcpy(str, st.str);
cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
警告:如果类中包含了使用new初始化的指针成员,则我们自己应定义一个复制构造函数,以复制指向的数据,而不是复制指针,这被成为深拷贝。浅拷贝仅仅只是复制指针的值。
4.赋值运算符:
object2 = object1;
当我们使用以上语句用object1对object2进行初始化时,实际是分两步来处理:先使用复制构造函数创建一个临时对象,然后通过复制运算符将临时对象复制到新对象中。在第一步中,我们使用我们自己定义的复制构造函数来进行深拷贝;在第二步中,如果我们不自己定义赋值运算符时,我们进行的会是跟默认的复制构造函数一样的浅拷贝,所以我们在这里需要自己定义赋值运算符进行深拷贝。
这里定义的赋值运算符如下:
StringBad & StringBad::operator=(const StringBad & sb)
{
if(this == &sb)
return *this;
delete [] str;
length = sb.length;
str = new char[length + 1];
strcpy(str, sb.str);
return *this;
}
5.整个项目实例代码:
文件结构:
din.h代码:
#ifndef DIN_H_INCLUDED
#define DIN_H_INCLUDED
#include <iostream>
using namespace std;
class StringBad
{
private:
char * str;
int length;
static int objectNum; //静态数据成员,为所有的对象所共享
public:
StringBad(); //默认构造函数
StringBad(const char * s); //构造函数
~StringBad(); //析构函数
StringBad(const StringBad & st); //复制构造函数,用于将一个对象用令一个对象来初始化,如object1 = object2
StringBad & operator=(const StringBad & sb);
friend ostream & operator<<(ostream & os, const StringBad & sb);
};
#endif // DIN_H_INCLUDED
din.cpp代码:
#include <iostream>
#include <cstring>
#include "din.h"
using namespace std;
int StringBad::objectNum = 0; //静态数据成员
StringBad::StringBad() //默认构造函数
{
length = 4;
str = new char[4];
strcpy(str, "c++");
objectNum++;
cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
StringBad::StringBad(const StringBad & st) //复制构造函数,用于将一个对象用于初始化另外一个对象,如object1 = object2
{
objectNum++;
length = st.length;
str = new char[length + 1];
strcpy(str, st.str);
cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
StringBad::StringBad(const char * s) //构造函数
{
length = strlen(s);
str = new char[length + 1];
strcpy(str, s);
objectNum++;
cout << "Object" << objectNum << ": " << str << " is created." << endl;
}
StringBad & StringBad::operator=(const StringBad & sb)
{
if(this == &sb)
return *this;
delete [] str;
length = sb.length;
str = new char[length + 1];
strcpy(str, sb.str);
return *this;
}
StringBad::~StringBad() //析构函数
{
objectNum--;
cout << str << " is deleted." << endl;
delete [] str;
cout << objectNum << " objects left." << endl;
}
ostream & operator<<(ostream & os, const StringBad & sb) //重载<<运算符
{
os << sb.str << endl;
return os;
}
main.cpp代码:
#include <iostream>
#include "din.h"
using namespace std;
int main()
{
StringBad sb1, sb2("abcde"), sb4;
StringBad sb3 = sb2;
sb4 = sb2;
cout << sb1;
cout << sb2;
cout << sb3;
cout << sb4;
cout << endl;
return 0;
}
运行结果: