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

07-19 1251阅读

文章目录

  • 一、平凡类型与非平凡类型什么时候使用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 
            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 
            #include 
            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 
              #include 
              #include 
              #include 
              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 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 args;可以支持动态增加参数
              • [[nodiscard]]如果没有使用,则产生告警
                #include 
                #include 
                #include 
                #include 
                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 args;
                };
                template 
                struct [[nodiscard]] ConnectionBuilder : ConnectionBuilderBase {
                    [[nodiscard]] ConnectionBuilder &withAddress(std::string addr) {
                        serverAddress = addr;
                        return static_cast(static_cast(*this));
                    }
                    [[nodiscard]] ConnectionBuilder &withPort(int p) {
                        port = p;
                        return *this;
                    }
                    [[nodiscard]] ConnectionBuilder &withAddressAndPort(std::string addr) {
                        auto pos = addr.find(':');
                        serverAddress = addr.substr(0, pos);
                        port = std::stoi(addr.substr(pos + 1));
                        return static_cast(static_cast(*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 
                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 
                  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(food))
                      {   
                          eat->eat({5,100});
                      }
                      if (auto drink = dynamic_cast(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 
                  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(){}…
                  • 还有增加前向声明

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

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

                    • 还是会影响开闭原则
                    • 优点是如果增加接口,修改的地方不多:(1)struct FoodVisitor增加一个重载,(2)struct PengUser 去实现具体的访问行为
                      #include 
                      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
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]