您的位置:首页 > 编程语言 > C语言/C++

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,让编译器自动检查函数是否与基类函数完全匹配,从而正确实现方法重写。不加的话:如果发现函数返回值或者参数类型不同,则是在派生类中创建了新的方法,而不是重写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: