C++类设计原则
2017-01-09 23:26
190 查看
面向对象和与面向过程的比较
l 对象使数据和成员函数之间的结合更加紧密,更加有意义;l 对象更便于查找错误,因为操作都只局限于它们的对象;
l 对象可以对其他对象隐藏某些操作细节,从而使得这些操作不会受到其他对象的影响。
析构函数设置为virtual
作用:虚构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。如果析构函数不是虚函数,而程序执行时又要通过基类的指针去销毁派生类的动态对象,那么用delete销毁对象时,只调用了基类的析构函数,未调用派生类的析构函数。这样会造成销毁对象不完全。
析构函数为虚函数,当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。
自定义拷贝构造函数和赋值构造函数
场景:动态多维数组......拷贝构造函数是一种特殊的构造函数,也叫复制构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
注意:函数名称必须和类名称一致;虽然编译器自行会定义一个,但如果类中有动态内存分配,必须有一个自定义拷贝构造函数,以实现深层拷贝。
// 例子:一个表(Spreadsheet)中含有许多单元格(SpreadsheetCell.)。 #pragma once class SpreadsheetCell; class Spreadsheet { public: Spreadsheet(int inWidth, int inHeight); virtual ~Spreadsheet(); Spreadsheet(const Spreadsheet& src); Spreadsheet& operator=(const Spreadsheet& rhs); void setCellAt(int x, int y, const SpreadsheetCell& cell); SpreadsheetCell& getCellAt(int x, int y); private: bool inRange(int val, int upper); // void copyFrom(const Spreadsheet& src); int mWidth, mHeight; SpreadsheetCell** mCells; }; ////////////////////////////////////////////////////////// // 构造函数与析构函数: Spreadsheet::Spreadsheet(int inWidth, int inHeight) : mWidth(inWidth), mHeight(inHeight) { mCells = new SpreadsheetCell*[mWidth]; for (int i = 0; i < mWidth; i++) { mCells[i] = new SpreadsheetCell[mHeight]; } } Spreadsheet::~Spreadsheet() { for (int i = 0; i < mWidth; i++) { delete[] mCells[i]; } delete[] mCells; mCells = nullptr; } ////////////////////////////////////////////////////////// // 复制构造函数和赋值运算符 (初版) Spreadsheet::Spreadsheet(const Spreadsheet& src) { mWidth = src.mWidth; mHeight = src.mHeight; mCells = new SpreadsheetCell*[mWidth]; for (int i = 0; i < mWidth; i++) { mCells[i] = new SpreadsheetCell[mHeight]; } for (int i = 0; i < mWidth; i++) { for (int j = 0; j < mHeight; j++) { mCells[i][j] = src.mCells[i][j]; } } } // 注意:自赋值检测不仅是为了效率,更是为了正确性。赋值运算符首先删除左边对象的mCells,再给左边对象分配一个新的mCells。 Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs) { // check for self-assignment if (this == &rhs) { return *this; } // free the old memory for (int i = 0; i < mWidth; i++) { delete[] mCells[i]; } delete[] mCells; mCells = nullptr; // copy the new memory mWidth = rhs.mWidth; mHeight = rhs.mHeight; mCells = new SpreadsheetCell*[mWidth]; for (int i = 0; i < mWidth; i++) { mCells[i] = new SpreadsheetCell[mHeight]; } for (int i = 0; i < mWidth; i++) { for (int j = 0; j < mHeight; j++) { mCells[i][j] = rhs.mCells[i][j]; } } return *this; } // 这里的复制构造函数和赋值运算符十分相似,可以使用通用辅助方法实现简约。 void Spreadsheet::copyFrom(const Spreadsheet& src) { mWidth = src.mWidth; mHeight = src.mHeight; mCells = new SpreadsheetCell*[mWidth]; for (int i = 0; i < mWidth; i++) { mCells[i] = new SpreadsheetCell[mHeight]; } for (int i = 0; i < mWidth; i++) { for (int j = 0; j < mHeight; j++) { mCells[i][j] = src.mCells[i][j]; } } } // 复制构造函数和赋值运算符 (终版) Spreadsheet::Spreadsheet(const Spreadsheet &src) { copyFrom(src); } Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs) { // check for self-assignment if (this == &rhs) { return *this; } // free the old memory for (int i = 0; i < mWidth; i++) { delete[] mCells[i]; } delete[] mCells; mCells = nullptr; // copy the new memory copyFrom(rhs); return *this; } |
委托构造函数
#include <iostream> #include <string> using namespace std; class A { private: int i = 5; string str = "初始值"; public: A(){ str = "委托构造函数"; i = 99; } A(int ii) :A(){ // 不能写成AA(int ii):A(),i(ii) // 委托构造函数不能再利用初始化器初始化其他数据成员 i = ii; } void show(){ cout << "i=" << i << ",str=" << str << endl; } }; int main() { A a(10); a.show(); system("pause"); return 0; } |
Getters and Setters
Getters : 会改变的直接返回该类型,不应改变的返回const T&。Setters : 会改变的直接参数为该类型,不应改变的参数为const T&。
类比:不变的成员都应设置为const。
#include <string> class Employee { public: Employee(); virtual ~Employee(); void promote(int raiseAmount = 1000); void demote(int demeritAmount = 1000); void hire(); // Hires or rehires the employee void fire(); // Dismisses the employee void display() const;// Outputs employee info to console // Getters and setters void setFirstName(const std::string& firstName); const std::string& getFirstName() const; void setLastName(const std::string& lastName); const std::string& getLastName() const; void setEmployeeNumber(int employeeNumber); int getEmployeeNumber() const; void setSalary(int newSalary); int getSalary() const; bool getIsHired() const; private: std::string mFirstName; std::string mLastName; int mEmployeeNumber; int mSalary; bool mHired; }; |
对于const还有一个重要的用法,对于常对象,只能访问常函数。
Void A::func() const
Const A a;
a.func();
static设计原则
static 方法不属于特定对象,没有this指针。
注意:不能将static声明为const,这也是多余的,因为静态方法没有类的实例,因此不会改变类内部的值。
Override
子类的重写基类(以接口类居多)的虚函数时,建议在函数后面加入override,让编译器自动检查函数是否与基类函数完全匹配。原因:在函数后面加入override,让编译器自动检查函数是否与基类函数完全匹配,从而正确实现方法重写。不加的话:如果发现函数返回值或者参数类型不同,则是在派生类中创建了新的方法,而不是重写。