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

一、平凡类型与非平凡类型什么时候使用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

参考

相关推荐

  1. 什么情况需要进行网络安全等级保护?

    2024-07-18 04:54:03       25 阅读
  2. 什么情况需要用到动态IP

    2024-07-18 04:54:03       31 阅读
  3. 什么情况需要进行身份证实名认证?

    2024-07-18 04:54:03       25 阅读
  4. C++:基类中的函数什么情况声明为虚函数

    2024-07-18 04:54:03       35 阅读
  5. 什么情况导致索引失效

    2024-07-18 04:54:03       31 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-18 04:54:03       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-18 04:54:03       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-18 04:54:03       45 阅读
  4. Python语言-面向对象

    2024-07-18 04:54:03       55 阅读

热门阅读

  1. 理解 App Store 审核规则 3.2(f):预防被拒绝的方法

    2024-07-18 04:54:03       19 阅读
  2. VINS介绍

    2024-07-18 04:54:03       20 阅读
  3. CST高频仿真的网格技术

    2024-07-18 04:54:03       30 阅读
  4. 泰勒展开的推导及应用

    2024-07-18 04:54:03       20 阅读
  5. kotlin get set

    2024-07-18 04:54:03       18 阅读
  6. 网络安全-网络安全及其防护措施1

    2024-07-18 04:54:03       18 阅读
  7. SQL用户权限正则表达式设计思路

    2024-07-18 04:54:03       17 阅读
  8. C++ Primer:2.6 自定义数据结构

    2024-07-18 04:54:03       21 阅读