【c++11】什么情况下需要封装set/get

news2024/9/20 18:42:01

文章目录

  • 一、平凡类型与非平凡类型什么时候使用set/get
    • 1.平凡类型
    • 2.非平凡类型
  • 二、构造函数参数较多解决办法
    • 1.把所有参数放到一个结构体里面
    • 2.使用build设计模式
  • 三、如果构造函数众多(参数很多)
    • 1.模仿make_unique,就地构造
    • 2.基于build设计模式只定义移动版本的成员函数
  • 三、不同子类需要实现不同的接口,如何设计?
    • 1.使用RTTI
    • 2.定义接口接管RTTI
    • 3.使用访问者模式接管RTTI
  • 参考

一、平凡类型与非平凡类型什么时候使用set/get

1.平凡类型

平凡类型,里面的成员不使用set/get模式

C++版本:C++26

#include <print>

struct Point {
    double x;
    double y;

    Point operator+(Point const &other) const {
        return Point(x + other.x, y + other.y);
    }
};

int main() {
    Point a = Point{ .x = 1, .y = 2 }; // 等价于 Point{1, 2}
    Point b = Point{ .x = 2, .y = 3 }; // 等价于 Point{2, 3}
    Point c = a + b;
    std::println("{} {}", c.x, c.y);
    c.x = 1;
    return 0;
}

测试:

Program returned: 0
Program stdout
3 5

2.非平凡类型

非平凡类型,防止用户修改里面的成员,造成类不可以用,封装成set/get

#include <print>
#include <cstddef>

struct Vector {
private:
    int *m_data;
    size_t m_size;

public:
    Vector() : m_data(new int[4]), m_size(4) {}

    void setSize(size_t newSize) {
        m_size = newSize;
        delete[] m_data;
        m_data = new int[newSize];
    }

    int *data() const {
        return m_data;
    }

    size_t size() const {
        return m_size;
    }
};

int main() {
    Vector v;
    v.setSize(14);
    v.setSize(11);
    return 0;
}

测试:

在这里插入代码片

二、构造函数参数较多解决办法

1.把所有参数放到一个结构体里面

要点:

  • 参数里面的某些配置需要绑定在一起使用,则把这些封装成optional
  • connection仅仅管理fd,在包装参数的类中调用connect,构造一个connection
#include <optional>
#include <print>
#include <chrono>
#include <string>

using namespace std::chrono_literals;

struct Connection {
    int fd;

    explicit Connection(int fd_) : fd(fd_) {
    }
};

struct ConnectionBuilder {
    std::string serverAddress;
    int port;
    struct SSHParams {
        std::string sshCertPath = "";
        std::string sshPKeyPath = "";
        std::string sshCAFilePath = "";
    };
    std::optional<SSHParams> useSSH;
    std::string username = "admin";
    std::string password = "password";
    bool enableFastTCPOpen = true;
    int tlsVersion = 1;
    std::chrono::seconds connectTimeout = 10s;
    std::chrono::seconds readTimeout = 5s;

    Connection connect() {
        int fd = 0;
        // fd = open(serverAddress, port);
        return Connection(fd);
    }
};

Connection c = ConnectionBuilder{
             .serverAddress = "localhost",
             .port = 8080,
             .useSSH = std::nullopt,
         }.connect();

int main() {

    return 0;
}

2.使用build设计模式

多个参数的builder设计模式,同样可以解决某些参数需要绑定设置

  • 给ConnectionBuilder 增加模板参数,用于标记什么时候能构造connection,因为前面with都是指定需要使用的参数嘛
  • std::vector<std::string> args;可以支持动态增加参数
  • [[nodiscard]]如果没有使用,则产生告警
#include <optional>
#include <chrono>
#include <string>
#include <vector>

using namespace std::chrono_literals;

struct Connection {
    int fd;

    explicit Connection(int fd_) : fd(fd_) {
    }

    Connection &read();
};

struct ConnectionBuilderBase {
    std::string serverAddress;
    int port;
    bool useSSH = false;
    std::string sshCertPath = "";
    std::string sshPKeyPath = "";
    std::string sshCAFilePath = "";
    std::string username = "admin";
    std::string password = "password";
    bool enableFastTCPOpen = true;
    int tlsVersion = 1;
    std::chrono::seconds connectTimeout = 10s;
    std::chrono::seconds readTimeout = 5s;
    std::vector<std::string> args;
};

template <bool Ready = false>
struct [[nodiscard]] ConnectionBuilder : ConnectionBuilderBase {
    [[nodiscard]] ConnectionBuilder<true> &withAddress(std::string addr) {
        serverAddress = addr;
        return static_cast<ConnectionBuilder<true> &>(static_cast<ConnectionBuilderBase &>(*this));
    }

    [[nodiscard]] ConnectionBuilder &withPort(int p) {
        port = p;
        return *this;
    }

    [[nodiscard]] ConnectionBuilder<true> &withAddressAndPort(std::string addr) {
        auto pos = addr.find(':');
        serverAddress = addr.substr(0, pos);
        port = std::stoi(addr.substr(pos + 1));
        return static_cast<ConnectionBuilder<true> &>(static_cast<ConnectionBuilderBase &>(*this));
    }

    [[nodiscard]] ConnectionBuilder &withSSH(std::string cert, std::string pkey, std::string caf = "asas") {
        useSSH = true;
        sshCertPath = cert;
        sshPKeyPath = pkey;
        sshCAFilePath = caf;
        return *this;
    }

    [[nodiscard]] ConnectionBuilder &addArg(std::string arg) {
        args.push_back(arg);
        return *this;
    }

    [[nodiscard]] Connection connect() {
        static_assert(Ready, "你必须指定 addr 参数!");
        int fd = 0;
        // fd = open(serverAddress, port);
        return Connection(fd);
    }
};

Connection c = ConnectionBuilder<>()
    .withSSH("1", "2")
    .addArg("asas")
    .addArg("bsbs")
    .withAddressAndPort("localhost:8080")
    .addArg("baba")
    .connect();

int main() {

    return 0;
}

三、如果构造函数众多(参数很多)

1.模仿make_unique,就地构造

struct Cake {
    int handle;

    explicit Cake(int han) : handle(han) {}

    static Cake makeOrig() {
        // 构造原味蛋糕
        int han = 0;
        return Cake(han);
    }

    static Cake makeChoco(double range) {
        // 构造巧克力蛋糕
        int han = (int)range;
        return Cake(han);
    }

    static Cake makeMoca(int flavor) {
        // 构造抹茶味蛋糕
        int han = flavor;
        return Cake(han);
    }
};

Cake origCake = Cake::makeOrig();
Cake chocoCake = Cake::makeChoco(1.0);
Cake matchaCake = Cake::makeMoca(1);

int main() {

    return 0;
}

2.基于build设计模式只定义移动版本的成员函数

右值引用版本的build设计模式,如果涉及到管理资源的类,可以使用这个


#include <utility>

struct [[nodiscard]] Cake {
    int handle;

    Cake() {}

    [[nodiscard]] Cake &&setOrig() && {
        // 构造原味蛋糕
        handle = 0;
        return std::move(*this);
    }

    [[nodiscard]] Cake &&setChoco(double range) && {
        // 构造巧克力蛋糕
        handle = (int)range;
        return std::move(*this);
    }

    [[nodiscard]] Cake &&setMoca(int flavor) && {
        // 构造抹茶味蛋糕
        handle = flavor;
        return std::move(*this);
    }

    Cake(Cake &&) = default;
    Cake(Cake const &) = delete;
};

void func(Cake &&c) {}
void func(Cake const &c);

Cake origCake = Cake().setOrig().setChoco(1.0);
Cake chocoCake = Cake().setChoco(1.0);
Cake matchaCake = Cake().setMoca(1);
int main() {
    Cake c;
    std::move(c).setOrig();
    Cake().setOrig();
    func(std::move(c));

    return 0;
}

三、不同子类需要实现不同的接口,如何设计?

如果不同的子类需要实现不同的接口,就把这些接口单独拎出来分别使用接口继承。

  • 注意:使用虚继承,否则padding类就有两个food虚基类
  • C++多用接口继承,少用实现继承

1.使用RTTI

使用dynamic_cast统一接管

#include <print>

struct EatParams {
    int amount;
    int speed;
};

struct DrinkParams {
    int volume;
    int temperature;
};


struct Food {
    virtual ~Food() = default;
};


struct Drinkable : virtual Food {
    virtual void drink(DrinkParams drinkParams) = 0;
};

struct Eatable : virtual Food {
    virtual void eat(EatParams eatParams) = 0;
};



struct Cake : Eatable {
    void eat(EatParams eatParams) override {
        std::println("Eating cake...");
        std::println("Amount: {}", eatParams.amount);
        std::println("Speed: {}", eatParams.speed);
    }
};

struct Milk : Drinkable {
    void drink(DrinkParams drinkParams) override {
        std::println("Drinking milk...");
        std::println("Volume: {}", drinkParams.volume);
        std::println("Temperature: {}", drinkParams.temperature);
    }
};

struct Pudding : Eatable, Drinkable {
    void eat(EatParams eatParams) override {
        std::println("Eating pudding...");
        std::println("Amount: {}", eatParams.amount);
        std::println("Speed: {}", eatParams.speed);
    }

    void drink(DrinkParams drinkParams) override {
        std::println("Drinking pudding...");
        std::println("Volume: {}", drinkParams.volume);
        std::println("Temperature: {}", drinkParams.temperature);
    }
};



void dailyRun(Food* food)
{
    if (auto eat = dynamic_cast<Eatable*>(food))
    {   
        eat->eat({5,100});
    }

    if (auto drink = dynamic_cast<Drinkable*>(food))
    {   
        drink->drink({5,100});
    }


}




int main() {
    Cake cake;
    Milk milk;
    Pudding pudding;
    dailyRun(&cake);
    dailyRun(&milk);
    dailyRun(&pudding);


    return 0;
}

测试:

Program returned: 0
Program stdout
Eating cake...
Amount: 5
Speed: 100
Drinking milk...
Volume: 5
Temperature: 100
Eating pudding...
Amount: 5
Speed: 100
Drinking pudding...
Volume: 5
Temperature: 100

2.定义接口接管RTTI

#include <print>

struct EatParams {
    int amount;
    int speed;
};

struct DrinkParams {
    int volume;
    int temperature;
};

struct Drinkable;
struct Eatable;

struct Food {
    virtual ~Food() = default;

    virtual Drinkable* toDrinkable()
    {
        return nullptr;
    }
    

    virtual Eatable* toEatable()
    {
        return nullptr;
    }
    

};


struct Drinkable : virtual Food {
    virtual void drink(DrinkParams drinkParams) = 0;

    Drinkable* toDrinkable() override
    {
        return this;
    }
};

struct Eatable : virtual Food {
    virtual void eat(EatParams eatParams) = 0;

    Eatable* toEatable() override
    {
        return this;
    }
};



struct Cake : Eatable {
    void eat(EatParams eatParams) override {
        std::println("Eating cake...");
        std::println("Amount: {}", eatParams.amount);
        std::println("Speed: {}", eatParams.speed);
    }
};

struct Milk : Drinkable {
    void drink(DrinkParams drinkParams) override {
        std::println("Drinking milk...");
        std::println("Volume: {}", drinkParams.volume);
        std::println("Temperature: {}", drinkParams.temperature);
    }
};

struct Pudding : Eatable, Drinkable {
    void eat(EatParams eatParams) override {
        std::println("Eating pudding...");
        std::println("Amount: {}", eatParams.amount);
        std::println("Speed: {}", eatParams.speed);
    }

    void drink(DrinkParams drinkParams) override {
        std::println("Drinking pudding...");
        std::println("Volume: {}", drinkParams.volume);
        std::println("Temperature: {}", drinkParams.temperature);
    }
};



void dailyRun(Food* food)
{
    if (auto eat = food->toEatable())
    {   
        eat->eat({5,100});
    }

    if (auto drink = food->toDrinkable())
    {   
        drink->drink({5,100});
    }


}




int main() {
    Cake cake;
    Milk milk;
    Pudding pudding;
    dailyRun(&cake);
    dailyRun(&milk);
    dailyRun(&pudding);


    return 0;
}

但是还是违背开闭原则,如果在food的基础上增加接口,修改的地方不少

  • 在struct Food处需要修改,增加virtual Layable* toLayable(){}…
  • 还有增加前向声明
    在这里插入图片描述

3.使用访问者模式接管RTTI

  • 还是会影响开闭原则
  • 优点是如果增加接口,修改的地方不多:(1)struct FoodVisitor增加一个重载,(2)struct PengUser 去实现具体的访问行为
#include <print>

struct EatParams {
    int amount;
    int speed;
};

struct DrinkParams {
    int volume;
    int temperature;
};


//访问者模式特点:需要访问的数据对象构成重载
struct FoodVisitor {
    virtual void visit(struct Eatable *eat) {}
    virtual void visit(struct Drinkable *drink) {}
    virtual ~FoodVisitor() = default;
};

struct Food {
	//最根本的虚基类需要定义accept接口去接受这个访问者
    virtual void accept(FoodVisitor *visitor) = 0;
    virtual ~Food() = default;
};

#define DEF_FOOD_ACCEPT void accept(FoodVisitor *visitor) override { visitor->visit(this); }


struct Drinkable : virtual Food {
    virtual void drink(DrinkParams drinkParams) = 0;

    DEF_FOOD_ACCEPT
};

struct Eatable : virtual Food {
    virtual void eat(EatParams eatParams) = 0;

    DEF_FOOD_ACCEPT
};

struct Cake : Eatable {
    void eat(EatParams eatParams) override {
        std::println("Eating cake...");
        std::println("Amount: {}", eatParams.amount);
        std::println("Speed: {}", eatParams.speed);
    }
};

struct Milk : Drinkable {
    void drink(DrinkParams drinkParams) override {
        std::println("Drinking milk...");
        std::println("Volume: {}", drinkParams.volume);
        std::println("Temperature: {}", drinkParams.temperature);
    }
};

struct Pudding : Eatable, Drinkable {
    void eat(EatParams eatParams) override {
        std::println("Eating pudding...");
        std::println("Amount: {}", eatParams.amount);
        std::println("Speed: {}", eatParams.speed);
    }

    void drink(DrinkParams drinkParams) override {
        std::println("Drinking pudding...");
        std::println("Volume: {}", drinkParams.volume);
        std::println("Temperature: {}", drinkParams.temperature);
    }

    void accept(FoodVisitor *visitor) override {
        Eatable::accept(visitor);
        Drinkable::accept(visitor);
    }
};

//实际的访问者实现如何去访问:具体的访问行为
struct PengUser : FoodVisitor {
    void visit(Eatable *eat) override {
        eat->eat({5, 10});
    }

    void visit(Drinkable *drink) override {
        drink->drink({10, 20});
    }
};

void pengEat(Food *food) {
    PengUser user;
    /*
	一般都是user.eat(),user.drink()....访问者模式刚好相反
	*/
    food->accept(&user);
    food->accept(&user);
    food->accept(&user);
}

int main() {
    Cake cake;
    Milk milk;
    Pudding pudding;
    pengEat(&cake);
    pengEat(&milk);
    pengEat(&pudding);
    return 0;
}

测试:

Program returned: 0
Program stdout
Eating cake...
Amount: 5
Speed: 10
Eating cake...
Amount: 5
Speed: 10
Eating cake...
Amount: 5
Speed: 10
Drinking milk...
Volume: 10
Temperature: 20
Drinking milk...
Volume: 10
Temperature: 20
Drinking milk...
Volume: 10
Temperature: 20
Eating pudding...
Amount: 5
Speed: 10
Drinking pudding...
Volume: 10
Temperature: 20
Eating pudding...
Amount: 5
Speed: 10
Drinking pudding...
Volume: 10
Temperature: 20
Eating pudding...
Amount: 5
Speed: 10
Drinking pudding...
Volume: 10
Temperature: 20

参考

  • code
  • 【C/C++】什么情况下需要封装get/set

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1929050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Typescript】Interface和type的区别;探讨为什么interface赋值给Record需要索引签名

一、Interface与type的区别 1、Interface可以声明合并&#xff0c;type不行 声明同名的类型&#xff0c;Interface同名会合并&#xff0c;而type重名会报错 2、类型扩展的方式不同 Interface基于extends继承扩展基类类型&#xff0c;而type利用 & 扩展 3、type可以被基础…

使用geoipupdate自动更新GeoIP数据库

一、 什么是 GeoIP&#xff1f; 通过在数据库中将地理位置和 IP 地址相互映射&#xff0c;软件程序便可以使用 IP 地址来确定其对应的地理位置&#xff0c;其中包括国家/地区、州/省、城市、邮政编码、纬度/经度、ISP、区号和其他信息。 很多软件都使用 MaxMind 的数据库对 IP…

Hive的基本操作(查询)

1、基础查询 基本语法 select 字段列表|表达式|子查询 from 表(子查询|视图|临时表|普通表) where [not] 条件A and|or 条件B --先&#xff1a;面向原始行进行筛选 group by 字段A[,字段B,...] > 分组【去重处理】 having 聚合条件(非原始字段条件) --再&#x…

超市管理系统 需求分析与设计 UML 方向

一、项目介绍 1.1项目背景 随着经济一体化和电子商务的迅速发展&#xff0c;网络传播信息的速度打破了传统信息传递的模式&#xff0c;互联网的高速发展和计算机应用在各个高校进展迅速&#xff0c;更多信息化产品的突飞猛进&#xff0c;让现代的管理模式也发生了巨大的变化&…

Catena-x标准解读:CX-0005 Item Relationship Service API 2.1.1 项目关系服务

1 介绍 如今&#xff0c;大量数据存储在一个行业供应链的不同参与者之间。通过将数据与其相关上下文连接起来&#xff0c;可以极大地提高数据的价值。 为了实现跨企业链接数据&#xff0c;应用了方面模型、标准化协议和连接数据的标准化方法等标准。Catena-X为访问跨企业链接…

Python学习:实现Python项目并学习如何进行(附70个项目源码)

实现Python项目并学习如何进行&#xff0c;是一个循序渐进的过程&#xff0c;涵盖了多个方面&#xff0c;包括基础知识的学习、技能的提升、项目的规划和实施等。以下是一个基本的指南&#xff0c;帮助你开始学习并实现Python项目&#xff1a; 1. 学习Python基础知识 语法与基…

Linux介绍以及常用命令

目录 1. 什么是Linux 2. Linux的种类 3. CentOS7 超详细的CentOS7.4下载与图文安装 4. 常用的网络适配器种类 5. 安装ssh客户端软件 6. CentOS7的目录结构 7. Linux常用命令详解 1.目录切换命令 -- cd 2. 查看目录下的内容 -- ls 3. 查看当前所在的目录 -- pwd 4. 创…

如何在gitee上创建远程仓库?

登录gitee网站后 填写自己的仓库信息后点击创建 然后来到一个新的界面可以看到自己的仓库地址 这样一个空白的仓库就建立好了 也可以按需选择初始化仓库

结合类型信息(1)——特征丰富的知识库补全网络

1 引言 1.1 问题 首先&#xff0c;隐式对应关系的问题。在联合嵌入模型中&#xff0c;知识库和文本数据之间的关系并不是直接给出的&#xff0c;而是需要通过实体在文本中的共现来推断。这意味着模型必须从上下文中学习实体之间的潜在关联&#xff0c;而不是基于显式的标签或…

利用css设计一套漩涡式网格(grid)布局

在Web开发中,CSS Grid布局系统为我们提供了前所未有的灵活性和控制力,使得创建复杂的二维布局变得轻而易举。今天,我们将探讨一种特殊的Grid布局——漩涡式布局,并通过一个具体的HTML和CSS示例来深入了解其实现方式。 漩涡式Grid布局概述 漩涡式Grid布局是一种视觉上呈现出…

【Linux】Ubuntu 漏洞扫描与修复的吃瘪经历

自从上次“劫持”事情后&#xff0c;项目经理将所有跟安全相关的都推给我了&#xff08;不算 KPI 又要被白嫖&#xff0c;烦死了&#xff09;。这次客户又提了一个服务器安全扫描和漏洞修复的“活”&#xff0c;我这边顺手将过程记录一下&#xff0c;就当经验总结跟各位分享一下…

一群追星星的人,对 AI 的盼与怕

面对 AI&#xff0c;有人害怕&#xff0c;有人期盼。 “AI 和画画的、开网约车的、写东西的人有仇吗&#xff1f;”近来成了很多从业者的心声。大模型技术驱动了 AI 的能力进化过临界点&#xff0c;我们普通人根本就跟不上&#xff0c;或快或慢被淘汰。看起来&#xff0c;AI 正…

[ACM独立出版] 2024年虚拟现实、图像和信号处理国际学术会议(VRISP 2024,8月2日-4)

2024年虚拟现实、图像和信号处理国际学术会议&#xff08;VRISP 2024&#xff09;将于2024年8月2-4日在中国厦门召开。 VRISP 2024将围绕“虚拟现实、图像和信号处理”的最新研究领域&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师等提供…

SpringBoot中动态注册Bean的方式

测试环境&#xff0c;本文源码 Java&#xff1a;8SpringBoot&#xff1a;2.5.14示例场景&#xff1a;动态注册ProxyServlet&#xff0c;间接实现类似于Nginx的反向代理功能 先理解如何实现动态注册 Bean 。 由于在 SpringBoot 中&#xff0c;先进行 Bean 的定义&#xff0c;…

【前端4】表单 编辑模式、只读模式:HTML的`readonly`、el-input的v-if=“isEdit“

【前端】表单 编辑模式、只读模式 写在最前面一、什么是编辑模式与只读模式&#xff1f;应用场景编辑模式只读模式 二、编辑模式的实现例子只读模式的实现动态切换模式使用HTML的readonly属性使用Vue.js的v-if指令 三、前后端交互 <template>代码块两个字段独立是否直接与…

Auto CAD 2020下载安装教程怎么安装如何使用

Auto CAD 2020下载安装教程 下载链接&#xff1a;https://pan.baidu.com/s/16WR6WdkWqn8WnShZHu8S5Q?pwdhdh7 提取码&#xff1a;hdh7 解压后看到这些文件 进入第一个文件夹后看到安装包&#xff0c;如果缺少了第二个文件&#xff0c;先关闭杀毒软件后重新解压即可 进入…

超全整理,数据管理CDMP认证介绍

CDMP认证概述 CDMP&#xff08;Certified Data Management Professional&#xff09;认证&#xff0c;全称数据管理专业人士认证&#xff0c;是由国际数据管理协会&#xff08;DAMA International&#xff09;推出的权威认证。该认证旨在全面评估个人在数据管理和治理领域的专…

PyMongo Sort 操作:提升你的数据查询效率

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

最新PHP自助商城源码,彩虹商城源码

演示效果图 后台效果图 运行环境&#xff1a; Nginx 1.22.1 Mysql5.7 PHP7.4 直接访问域名即可安装 彩虹自助下单系统二次开发 拥有供货商系统 多余模板删除 保留一套商城,两套发卡 源码无后门隐患 已知存在的BUG修复 彩虹商城源码&#xff1a;下载 密码:chsc 免责声明&…

[MySQL][表的增删查改][二][Retrieve][SELECT][WHERE]详细讲解

目录 1.Retrieve1.基本语法2.SELECT列1.全列查询2.查询字段为表达式3.为查询结果指定别名4.结果去重 3.WHERE条件1.比较运算符2.逻辑运算符3.示例 4.结果排序1.基本语法2.示例 5.筛选分页结果 1.Retrieve 1.基本语法 SELECT [DISTINCT] * | {column [, column] ...} [FROM ta…