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

文章目录

  • 一、平凡类型与非平凡类型什么时候使用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.mzph.cn/news/872622.shtml

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

相关文章

Missing script:‘dev‘

场景&#xff1a; npm run dev 原因&#xff1a;没有安装依赖&#xff0c;可用镜像安装&#xff08;详见下图ReadMe 蓝色字体&#xff09;&#xff0c;没安装依赖可从package-lock.json文件是否存在看出&#xff0c;存在则有依赖 解决&#xff1a;

二叉树、B树/B-树

二叉树 在中文语境中,节点结点傻傻分不清楚,故后文以 node 代表 "结点",root node 代表根节点,child node 代表 “子节点” 二叉树是诸多树状结构的始祖,至于为什么不是三叉树,四叉树,或许是因为计算机只能数到二吧,哈哈,开个玩笑。二叉树很简单,每个 no…

useState函数

seState是一个react Hook(函数)&#xff0c;它允许我们像组件添加一个状态变量&#xff0c;从而控制影响组件的渲染结果 数据驱动试图 本质&#xff1a;和普通JS变量不同的是&#xff0c;状态变量一旦发生变化组件的视图UI也会随着变化(数据驱动试图) 使用 修改状态 注意&am…

单链表算法 - 链表分割

链表分割_牛客题霸_牛客网现有一链表的头指针 ListNode* pHead&#xff0c;给一定值x&#xff0c;编写一段代码将所有小于x的。题目来自【牛客题霸】https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70思路: 代码: /* struct ListNode {int val;struct List…

英福康INFICON TranspectorWare v3 RGA软件操作说明

英福康INFICON TranspectorWare v3 RGA软件操作说明

Python一对一辅导答疑|Rust 德国

你好&#xff0c;我是悦创。 下面是答疑内容。 在 Rust 中&#xff0c;方法的调用方式通常取决于它们是如何定义的。在你的例子中&#xff0c;print_drink方法最初是作为一个接受Drink类型实例作为参数的关联函数&#xff08;类似于静态方法&#xff09;定义的。后来&#xff…

供应链管理(SCM):如何在颜值和体验上发力

要在供应链管理系统&#xff08;SCM&#xff09;中在颜值和体验上发力&#xff0c;让用户感觉耳目一新&#xff0c;可以采取以下措施&#xff1a; 界面设计优化&#xff1a; 对供应链管理系统的界面进行优化&#xff0c;注重界面的美观、简洁和易用性。采用现代化的设计风格、…

技能 | postman接口测试工具安装及使用

哈喽小伙伴们大家好!今天来给大家分享一款轻量级,高效好用的接口测试工具-postman. Postman是一个流行的API开发工具&#xff0c;主要用于测试、开发和文档化API。以下是关于Postman的介绍及其主要使用场景&#xff1a; Postman介绍&#xff1a; 1. 功能丰富的API客户端&#…

在SpringCloud中如何轻松实现微服务间的通信

在Spring Cloud中&#xff0c;实现微服务间的通信非常简单。Spring Cloud提供了多种方式来进行微服务之间的通信&#xff0c;包括使用RestTemplate、Feign、Ribbon、Eureka等组件。下面我将详细介绍这些方式的使用方法。 使用RestTemplate进行通信&#xff1a; RestTemplate是S…

django报错(一):python manage.py makemigrations,显示“No changes detected”

执行python manage.py makemigrations命令无任何文件生成&#xff0c;结果显示“No changes detected”。 解决方案一&#xff1a; 1、执行命令&#xff1a;python manage.py makemigrations –empty appname 2、删除其中的0001_initial.py文件&#xff08;因为这个文件内容是…

【docker 部署springboot项目】

一、docker安装 1.检查Linux内核版本高于3.10才可安装 uname -r 2. 卸载旧版本 sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 3. 使用docker仓库进行安装 安装所需的软…

Qt MV架构-委托类

一、基本概念 与MVC模式不同&#xff0c;MV视图架构中没有包含一个完全分离的组件来处理与用户的交互。 一般地&#xff0c;视图用来将模型中的数据显示给用户&#xff0c;也用来处理用户的输入。为了获得更高的灵活性&#xff0c;交互可以由委托来执行。 这些组件提供了输入…

Python入门------pycharm加载虚拟环境

pycharm虚拟环境配置&#xff1a; 在按照前面的办法&#xff0c;配置好虚拟环境后,如果我们需要到虚拟环境开发&#xff0c;就需要给编译器配置虚拟环境 1.打开编译器&#xff0c;点击右下角的interpreter选项 2. 点击ADD Interpreter,添加虚拟环境 3. 因为我们使用的是原始…

欧式空间、傅里叶级数与希尔伯特空间的解释

欧式空间&#xff08;欧几里得空间&#xff09; 欧几里得几何就是中学学的平面几何、立体几何&#xff0c;在欧几里得几何中&#xff0c;两平行线任何位置的间距相等。 而中学学的几何空间一般是2维&#xff0c;3维&#xff08;所以&#xff0c;我们讨论余弦值、点间的距离、内…

数据库管理的艺术(MySQL):DDL、DML、DQL、DCL及TPL的实战应用(下:数据操作与查询)

文章目录 DML数据操作语言1、新增记录2、删除记录3、修改记录 DQL数据查询语言1、查询记录2、条件筛选3、排序4、函数5、分组条件6、嵌套7、模糊查询8、limit分页查询 集合操作union关键字和运算符in关键字any关键字some关键字all关键字 联合查询1、广义笛卡尔积2、等值连接3、…

【事件排查】网络问题排查H3C无线优化方案

目录 背景 问题一 排查思路 解决方法 问题二 排查思路 解决方法 背景 公司进行搬迁&#xff0c;网络进行了调整 基于上篇文章《H3C Intelligent Management Center无线认证新增设备如何配置》 来做了一些网络配置&#xff0c;公司后续出现以下2个问题&#xff1a; …

在Linux系统安装MySQL有多简单

MySQL 是一种流行的开源关系数据库管理系统&#xff0c;广泛应用于各种类型的应用程序和服务。本文将介绍在 Linux 上安装 MySQL 的多种方式&#xff0c;包括离线安装、使用 Docker 容器、通过 Helm Chart 安装在 Kubernetes 集群中等。 前言 无论你是在开发环境中测试&#…

设计模式-概述*

1.代码的质量的评判 可维护性&#xff1a;不破坏原有代码设计以及不引入新的bug的前提下&#xff0c;能够快速修改或新增代码&#xff1b;可读性&#xff1a;人类能理解的代码&#xff08;编程规范-命名、函数是否冗长、类是否过大等&#xff09;&#xff1b;可扩展性&#xff…

奥运火炬的三次传递 品牌精神的传承赓续丨陈忠伟董事长巴黎传递奥运圣火

7月14号&#xff0c;2024年巴黎奥运火炬在巴黎传递&#xff0c;中国企业家、恒源祥集团董事长兼总经理陈忠伟作为火炬手参与了传递&#xff0c;这也是他第三次参加奥运火炬的传递。 &#xff08;陈忠伟董事长点燃火炬&#xff09; 恒源祥从上世纪80年代起就开始赞助体育赛事&a…

持续集成04--Jenkins结合Gitee创建项目

前言 在持续集成/持续部署&#xff08;CI/CD&#xff09;的旅途中&#xff0c;Jenkins与版本控制系统的紧密集成是不可或缺的一环。本篇“持续集成03--Jenkins结合Gitee创建项目”将引导如何将Jenkins与Gitee&#xff08;一个流行的Git代码托管平台&#xff09;相结合&#xff…