成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C++高級編程:構建高效穩定接口與深入對象設計技巧

開發 前端
類是C++中的主要抽象單位,你應該將抽象原則應用于你的類,盡可能將接口與實現分離。具體來說,你應該使所有數據成員私有,并可選擇性地提供getter和setter方法。

一、建立穩定接口

類是C++中的主要抽象單位。你應該將抽象原則應用于你的類,盡可能將接口與實現分離。具體來說,你應該使所有數據成員私有,并可選擇性地提供getter和setter方法。這就是SpreadsheetCell類的實現方式:m_value是私有的,而公共的set()方法設置值,getValue()和getString()方法檢索值。

1.使用接口和實現類

即便采取了上述措施和最佳設計原則,C++語言本質上對抽象原則不友好。其語法要求你將公共接口和私有(或受保護的)數據成員及方法組合在一個類定義中,從而將類的一些內部實現細節暴露給其客戶端。這樣做的缺點是,如果你需要在類中添加新的非公開方法或數據成員,所有使用該類的客戶端都必須重新編譯。這在大型項目中可能成為負擔。

好消息是你可以讓你的接口更加干凈,并隱藏所有實現細節,從而實現穩定的接口。壞消息是這需要一些編碼工作。基本原則是為你想編寫的每個類定義兩個類:接口類和實現類。實現類與你在不采取此方法時編寫的類相同。接口類提供與實現類相同的公共方法,但它只有一個數據成員:指向實現類對象的指針。這被稱為pimp習語,私有實現習語,或橋接模式。接口類的方法實現簡單地調用實現類對象上的等效方法。

這樣的結果是,無論實現如何改變,都不會影響公共接口類。這減少了重新編譯的需要。如果實現(僅實現)發生變化,使用接口類的客戶端無需重新編譯。請注意,這種習語僅在單一數據成員是指向實現類的指針時才有效。如果它是按值數據成員,則在實現類定義發生變化時,客戶端必須重新編譯。

要在Spreadsheet類中使用此方法,請定義以下公共接口類,稱為Spreadsheet。

module;
#include <cstddef>
export module spreadsheet;
export import spreadsheet_cell;
import <memory>;

export class SpreadsheetApplication { };

export class Spreadsheet {
public:
    Spreadsheet(const SpreadsheetApplication& theApp, size_t width = MaxWidth, size_t height = MaxHeight);
    Spreadsheet(const Spreadsheet& src);
    Spreadsheet(Spreadsheet&&) noexcept;
    ~Spreadsheet();
    Spreadsheet& operator=(const Spreadsheet& rhs);
    Spreadsheet& operator=(Spreadsheet&&) noexcept;
    void setCellAt(size_t x, size_t y, const SpreadsheetCell& cell);
    SpreadsheetCell& getCellAt(size_t x, size_t y);
    size_t getId() const;
    static const size_t MaxHeight { 100 };
    static const size_t MaxWidth { 100 };
    void swap(Spreadsheet& other) noexcept;

private:
    class Impl;
    std::unique_ptr<Impl> m_impl;
};

export void swap(Spreadsheet& first, Spreadsheet& second) noexcept;

實現類Impl是一個私有嵌套類,因為除了Spreadsheet類之外,沒有人需要了解這個實現類。現在,Spreadsheet類只包含一個數據成員:指向Impl實例的指針。公共方法與舊的Spreadsheet類相同。

2.掌握類和對象

嵌套的Spreadsheet::Impl類在spreadsheet模塊的實現文件中定義。它應該對客戶端隱藏,因此不導出Impl類。Spreadsheet.cpp模塊實現文件如下開始:

module;
#include <cstddef

>
module spreadsheet;
import <utility>;
import <stdexcept>;
import <format>;
import <algorithm>;
using namespace std;

// Spreadsheet::Impl類定義。
class Spreadsheet::Impl {
    /* 為簡潔起見省略 */
};

// Spreadsheet::Impl方法定義。
Spreadsheet::Impl::Impl(const SpreadsheetApplication& theApp, size_t width, size_t height)
: m_id { ms_counter++ }
, m_width { min(width, Spreadsheet::MaxWidth) }
, m_height { min(height, Spreadsheet::MaxHeight) }
, m_theApp { theApp }
{
    m_cells = new SpreadsheetCell*[m_width];
    for (size_t i{ 0 }; i < m_width; i++) {
        m_cells[i] = new SpreadsheetCell[m_height];
    }
}
// 其他方法定義省略以簡潔。

Impl類幾乎具有與原始Spreadsheet類相同的接口。對于方法實現,需要記住Impl是一個嵌套類;因此,你需要指定作用域為Spreadsheet::Impl。所以,對于構造函數,它變成了Spreadsheet::Impl::Impl(...)。

由于Spreadsheet類具有指向實現類的unique_ptr,因此Spreadsheet類需要有用戶聲明的析構函數。由于我們不需要在此析構函數中執行任何操作,因此可以在實現文件中將其默認為:

Spreadsheet::~Spreadsheet() = default;

事實上,它必須在實現文件中默認,而不是直接在類定義中。原因是Impl類僅在Spreadsheet類定義中前向聲明;也就是說,編譯器知道將會有一個Spreadsheet::Impl類出現在某處,但此時它還不知道定義。因此,你不能在類定義中默認析構函數,因為編譯器會嘗試使用尚未定義的Impl類的析構函數。在這種情況下,對其他方法進行默認操作時也是如此,例如移動構造函數和移動賦值運算符。

二、實現Spreadsheet方法

Spreadsheet類的方法實現,如setCellAt()和getCellAt(),只是將請求傳遞給底層的Impl對象:

void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell) {
    m_impl->setCellAt(x, y, cell);
}

SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y) {
    return m_impl->getCellAt(x, y);
}

Spreadsheet的構造函數必須構造一個新的Impl以執行其工作:

Spreadsheet::Spreadsheet(const SpreadsheetApplication& theApp, size_t width, size_t height) {
    m_impl = make_unique<Impl>(theApp, width, height);
}

Spreadsheet::Spreadsheet(const Spreadsheet& src) {
    m_impl = make_unique<Impl>(*src.m_impl);
}

拷貝構造函數看起來有些奇怪,因為它需要從源Spreadsheet復制底層的Impl。拷貝構造函數接受一個Impl的引用,而不是指針,所以你必須解引用m_impl指針來獲取對象本身。

Spreadsheet賦值運算符必須同樣將賦值傳遞給底層的Impl:

Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs) {
    *m_impl = *rhs.m_impl;
    return *this;
}

賦值運算符中的第一行看起來有些奇怪。Spreadsheet賦值運算符需要將調用轉發給Impl賦值運算符,這只在你復制直接對象時運行。通過解引用m_impl指針,你強制執行直接對象賦值,這導致調用Impl的賦值運算符。

swap()方法簡單地交換單一數據成員:

void Spreadsheet::swap(Spreadsheet& other) noexcept {
    std::swap(m_impl, other.m_impl);
}

這種技術將接口與實現真正分離,是非常強大的。雖然一開始有些笨拙,但一旦習慣了,你會發現它很自然易用。然而,在大多數工作環境中,這不是常見做法,所以你可能會遇到同事的一些抵觸。支持這種做法的最有力論據不是分離接口的美學,而是如果類的實現發生變化,構建時間的加速。

三、注意

使用穩定的接口類,可以減少構建時間。將實現與接口分離的另一種方法是使用抽象接口,即只有純虛方法的接口,然后有一個實現該接口的實現類。這是下個主題。

責任編輯:趙寧寧 來源: coding日記
相關推薦

2010-01-11 10:28:51

C++編程

2024-01-03 13:38:00

C++面向對象編程OOP

2011-05-30 15:29:32

C++

2010-01-12 15:24:48

C++語言

2010-01-26 17:11:13

C++編程

2024-04-01 13:05:13

C++接口類開發

2011-07-10 15:26:54

C++

2024-01-22 00:10:00

C++接口編程

2011-07-13 16:36:11

C++

2024-09-24 10:41:57

MyBatis編程

2012-12-25 09:45:08

PythonWeb

2010-01-12 10:40:22

C++程序員

2025-01-27 00:54:31

2010-01-11 10:41:05

C++編程

2023-11-22 13:40:17

C++函數

2025-04-02 03:11:00

Python函數C++

2023-12-13 10:01:15

數據結構c++編程

2010-01-13 10:16:42

C++軟件

2025-06-30 02:22:00

C++高性能工具

2010-01-28 16:05:09

C++風格與技巧
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 一级高清免费毛片 | 91精品久久久久久久久中文字幕 | 羞视频在线观看 | 国产精品久久久久久高潮 | av色噜噜| 96国产精品久久久久aⅴ四区 | 国产精品18毛片一区二区 | 伊人网站在线观看 | 中文字字幕一区二区三区四区五区 | 久久国产精品一区二区三区 | 欧美99久久精品乱码影视 | 特黄一级 | 久久精品国产亚洲 | 韩日有码 | 精品一区电影 | 日本污视频 | 国产午夜精品视频 | 亚洲人成人一区二区在线观看 | 国产精品嫩草影院精东 | 亚洲一区在线观看视频 | 91精品久久久久久久久99蜜臂 | 亚洲大片在线观看 | 日韩综合网 | 欧美精品在线免费观看 | 久久精品一区 | 欧美成人自拍视频 | 黄色网址在线播放 | 99热.com | 国产黄色大片网站 | 亚洲国产aⅴ成人精品无吗 国产精品永久在线观看 | 天堂av在线影院 | av在线播放一区二区 | 一级免费毛片 | 亚洲欧美成人影院 | 国产亚洲日本精品 | 欧美日韩国产精品一区二区 | 久久综合九色综合欧美狠狠 | 少妇黄色| 亚洲日本一区二区三区四区 | 日本精品视频在线 | 亚洲精品视频一区二区三区 |