静态成员变量的声明
静态类成员变量是独立于类的对象而存储的。
声明时,须指明类型,使用作用域运算符,不使用关键字static
。
例如:int Class_Name::x = 1;
其次,初始化是在实现文件中,而不是在头文件中,否则可能会多次初始化。
一种例外情况是,静态数据成员为const类型或枚举类型。
c++自动提供的成员函数
默认构造函数
默认析构函数
复制构造函数
赋值运算符
何时需要自己定义上面的由编译器自动提供的成员函数?
- ① 需要更新静态的类成员。(比如,说每生成一个对象,
int count
自增,每一个对象的生命周期结束,count
自减) - ② 有需要动态申请内存的时候(使用
new
、malloc
)
默认构造函数
如果需要在新建一个对象的时候申请新的内存空间,必须显示的定义一个构造函数
默认析构函数
对于在新建对象时候申请的空间,必须在析构函数中释放。
复制构造函数
作用:新建一个对象并且初始化为现有对象,不过是浅复制,或者说仅仅是值的复制。
何时调用:类对象作函数参数、函数的返回值等等。
默认(隐式)构造函数的潜在问题:① 不会更新静态变量 ② 复制的仅仅是值,如果是动态申请的内存,同一块空间会被释放两次。
函数原型:ClassName(const ClassName &);
赋值运算符
作用:将一个已有的对象赋值给另一个对象(但不是一个为初始化的对象);
默认的赋值运算符的潜在问题:与复制构造函数类似。
函数原型:ClassName & ClassName::operator=(const ClassName &);
解决方案:
定义一个显示的赋值运算符定义,与显示定义的复制构造函数类似。
另外须注意:
-
- 先用
delete
释放掉目标对象之前控制的某些内存;
- 先用
-
- 避免赋值给自己,一是没有必要,二是释放内存操作可能造成数据的破坏;
-
- 返回一个对象的引用,方便连续赋值(
return *this;
)
- 返回一个对象的引用,方便连续赋值(
关于new
、delete
- 如果在构造函数中使用
new
来初始化成员指针,必须在析构函数中使用delete
。 new
和delete
、new[]
和delete[]
必须相互匹配。
举一个经典了例子;
如果用字符数组去实现一个自己的String类,那么,在一般情况下需要开辟多个字符空间,但是如果仅仅需要开辟一个一个字符空间的话,也必须使用new char[1]
。- 如果有多个构造函数,则必须使用相同的方式使用new,要么都带,要么都不带。
String
下面是按照书上的String类,自己对照的写的,很有价值。
头文件
#ifndef MY_STRING_STRING_H
#define MY_STRING_STRING_H
#include <iostream>
using std::istream;
using std::ostream;
class String {
private:
char* str;
int len;
static int numOfStrings;
static const int CINLIM = 80;
public:
String(); //默认构函数
String(const char*); //构造函数
String(const String&); //复制构造函数
~String();
int length() const {
return len;}
String & operator=(const String&); //用同类对象进行赋值
String & operator=(const char*); //用字面字符串赋值
char & operator[](int); //中括号运算符,返回引用是为了能够改变对应的字符值
const char operator[](int) const ; //对于常量对象,上面的方法不能保证对象不被修改,所以必须重载
friend bool operator<(const String&,const String&); //之所有使用友元而不是成员函数来重载,是为了 "xxx" 和 String& 的比较。
friend bool operator>(const String&,const String&); //字面字符串指针能够调用复制构造函数生成一个临时的String对象。
friend bool operator==(const String&,const String&);
friend ostream & operator<<(ostream&,const String&); //返回引用一是为了减少开销
friend istream & operator>>(istream&,String&); //二是因为iostream没有公有的复制构造函数
static int how_many();
};
#endif //MY_STRING_STRING_H
实现文件
#include "String.h"
//类的静态成员只能在实现文件中初始化(除非是const static的),不在头文件中是防止多次包含头文件时多次初始化
int String::numOfStrings = 0;
int String::how_many() {
return numOfStrings;
}
String::String() {
len = 0;
str = new char[1]; //不能是 new char;
str[0] = '\0';
++numOfStrings;
}
String::String(const char *s) {
len = strlen(s);
str = new char[len+1];
strcpy(str,s);
++numOfStrings;
}
String::String(const String &s) {
len = s.len;
str = new char[len+1];
strcpy(str,s.str);
++numOfStrings;
}
String::~String() {
--numOfStrings;
delete [] str;
}
String &String::operator=(const String &s) {
// 防止自己赋值给自己
if(this==&s){
return *this;
}
//将 str指向的堆空间中的字符串占的空间释放
delete [] str;
len = s.len;
str = new char[len+1];
strcpy(str,s.str);
return *this;
}
String &String::operator=(const char *s) {
delete [] str;
len = strlen(s);
str = new char[len+1];
strcpy(str,s);
return *this;
}
char &String::operator[](int pos) {
return str[pos];
}
const char String::operator[](int pos) const {
return str[pos];
}
bool operator<(const String &s1, const String &s2) {
return strcmp(s1.str,s2.str)<0;
}
bool operator>(const String &s1, const String &s2) {
return s1<s2;
}
bool operator==(const String &s1, const String &s2) {
return !(s1<s2) && !(s2<s1);
}
ostream &operator<<(ostream &os, const String &s) {
os<<s.str;
return os;
}
istream &operator>>(istream &is, String &s) {
char temp[String::CINLIM];
is.get(temp,String::CINLIM);
if(is) s = temp;
return is;
}