- 成员变量的命名风格
对于一般的命名来说在一定的情况中可能或造成命名冲突
- 示例:
class Date { public: Date(int year) { // 这里的year到底是成员变量,还是函数形参?指定不明,编译器会根据就近原则选择是函数形参变量 year = year; } private: int year; };
- 命名风格1:
class Date { public: Date(int year) { _year = year; } private: int _year; };
- 命名风格2:
class Date { public: Date(int year) { m_year = year; } private: int m_year; };
四、析构函数
引入:
和构造函数一样,因为一些类型的创建后需要自行释放动态开辟的空间(避免内存泄漏),为了避免忘记,C++也引入了析构函数
概念:
析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作(释放动态开辟的空间)
特性:
析构函数名是在类名前加上字符 ~
无参数无返回值
一个类有且只有一个析构函数,若未显式定义,系统会自动生成默认的析构函数(不能重载)
对象生命周期结束时,C++编译系统系统自动调用析构函数 (在C语言上的优化)
示例:
typedef int DataType; class SeqList { public: SeqList(int capacity = 10)//构造函数 { _pData = (DataType*)malloc(capacity * sizeof(DataType)); assert(_pData); _size = 0; _capacity = capacity; } ~SeqList()//析构函数 { cout << "~SeqList()" << endl; if (_pData) { free(_pData); // 释放堆上的空间 _pData = NULL; // 将指针置为空 _capacity = 0; _size = 0; } } private: int* _pData; size_t _size; size_t _capacity; }; int main() { SeqList sl; return 0; }
- 对于编译器生成的默认析构函数,对于内置类型不用处理(对象生命周期结束会自动销毁),对自定类型成员调用它的析构函数
注:一般来说如果成员变量没有动态开辟的空间,可以不用自己写,编译器自动生成的就足够用
- 示例:
class String { public: String(const char* str = "jack") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~String()" << endl; free(_str); } private: char* _str; }; class Person { private: String _name; int _age; }; int main() { Person p; return 0; }
五、拷贝构造函数
概念:
在创建对象时,创建一个与一个对象一某一样的新对象
只有单个形参,该形参是对本 类类型对象的引用**(一般常用const**修饰),在用已存在的类类型对象创建新对象时由编译器自动调用
特征:
拷贝构造函数是构造函数的一个重载形式(参数不同)
拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用(传值也是一种拷贝,会继续调用拷贝函数)
示例:
class Date { public: Date(int year = 1900, int month = 1, int day = 1)//构造函数 { _year = year; _month = month; _day = day; } Date(const Date& d)//拷贝构造函函数 { _year = d._year; _month = d._month; _day = d._day; } private: int _year; int _month; int _day; }; int main() { Date d1;//调用构造函数 Date d2(d1);//调用拷贝构造函数 return 0; }
- 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝
class Date { public: Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; int main() { Date d1; // 这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的。 Date d2(d1); return 0; }
- 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,对于像日期类这样的类(只有指针类型的类)是没必要的,编译器生成的足够使用了,而对于存在指针类型的成员函数则会存现问题
class String { public: String(const char* str = "xxxx") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String()//析构函数,进行释放动态开辟的空间 { cout << "~String()" << endl; free(_str); } private: char* _str; }; int main() { String s1("hello"); String s2(s1); }
- 解释:
当两个同类对象的成员指针变量同时指向一个动态开辟的空间,如果一个对象对这空间进行释放,而另一个对象却依旧保存着该地址(野指针),如果进行操作该空间(再次free等)则会造成程序崩溃