如何让Boost合约对C++客户端可见?构造器场景优化方案问询
核心需求梳理
你希望用Boost Contract实现设计-by-contract(DBC),核心诉求是合约对客户端可见(符合DBC的本质),但Boost Contract通常写在函数定义里,普通函数你已经用了“头文件放合约+私有实现”的临时方案,但卡在了构造器——因为父类没有默认构造,要么得在头文件暴露构造器实现,要么把合约藏在cpp里。你考虑过工厂方法,想知道有没有更优解。
构造器的优化方案
1. 用Boost Contract的构造器合约宏(推荐)
Boost Contract专门提供了构造器合约的宏BOOST_CONTRACT_CONSTRUCTOR,可以直接把合约写在头文件的类声明里,同时把构造器的实现(包括父类初始化)留在cpp中,完美解决合约可见性和实现隐藏的问题。
修改后的头文件test.h:
#pragma once #include <boost/contract.hpp> #include <memory> #include <string> class Parent { std::string m_name; public: Parent(std::string name); }; class Child : Parent { std::shared_ptr<Parent> m_parent; inline void newParent_impl(std::shared_ptr<Parent> parent); public: // 仅声明构造器 Child(std::string name, std::shared_ptr<Parent> parent); // 普通函数的现有方案保留 void newParent(std::shared_ptr<Parent> parent) { boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(parent); }); newParent_impl(parent); } // 在头文件定义构造器合约,客户端直接可见 BOOST_CONTRACT_CONSTRUCTOR( (Child)(std::string name, std::shared_ptr<Parent> parent) , precondition([&] { BOOST_CONTRACT_ASSERT(parent); }) ) };
对应的test.cpp:
#include "test.h" // 父类构造器实现 Parent::Parent(std::string name) : m_name(std::move(name)) {} // 私有实现函数 void Child::newParent_impl(std::shared_ptr<Parent> parent) { m_parent = std::move(parent); } // 子类构造器实现,无需重复写合约 Child::Child(std::string name, std::shared_ptr<Parent> parent) : Parent(std::move(name)), m_parent(std::move(parent)) {}
这个方案的好处是:合约完全在头文件暴露给客户端,构造器的初始化逻辑和实现细节依然隐藏在cpp里,不需要改动太多现有代码结构。
2. 优化版工厂方法
如果觉得宏的方式不够直观,工厂方法确实是可靠的选择,还能进一步封装——把构造器设为私有,只通过静态工厂方法创建对象,让合约完全集中在头文件的工厂方法中:
修改后的test.h:
#pragma once #include <boost/contract.hpp> #include <memory> #include <string> class Parent { std::string m_name; public: Parent(std::string name); }; class Child : Parent { std::shared_ptr<Parent> m_parent; // 构造器私有,客户端无法直接调用 Child(std::string name, std::shared_ptr<Parent> parent); inline void newParent_impl(std::shared_ptr<Parent> parent); public: // 静态工厂方法,在头文件暴露合约 static std::shared_ptr<Child> create(std::string name, std::shared_ptr<Parent> parent) { boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(parent); }); return std::make_shared<Child>(std::move(name), std::move(parent)); } void newParent(std::shared_ptr<Parent> parent) { boost::contract::check c = boost::contract::function() .precondition([&] { BOOST_CONTRACT_ASSERT(parent); }); newParent_impl(parent); } };
test.cpp内容和之前类似,只是构造器是私有实现即可。
这种方式彻底隔离了实现细节,客户端只能通过工厂方法创建对象,合约清晰可见,完全符合DBC的要求。
普通函数的小优化
你当前的普通函数方案(头文件放合约+私有实现)已经很合理,建议补充移动语义来避免不必要的拷贝,比如把参数传递改成std::move(name)、std::move(parent),提升性能。另外,如果newParent_impl的实现比较简单,可以声明为private inline并直接放在头文件;如果复杂,还是留在cpp里保持头文件简洁。
总结
- 优先选Boost Contract构造器宏:兼顾合约可见性和实现隐藏,改动最小。
- 偏好封装性选工厂方法:彻底隔离实现,合约集中管理。
- 普通函数的现有方案无需大改,加移动语义优化即可。
内容的提问来源于stack exchange,提问作者digitig

