C++复数类设计模式优化:统一类型以避免重复运算符实现
问题描述
我正在借助复数类学习设计模式,当前采用抽象类的实现方式:
namespace abstract{ class complex{ public: virtual ~complex() = 0; virtual double re() const = 0; // some other virtual function }; }; namespace cartesian{ class complex : public abstract::complex{ public: complex(){} ~complex(){} double re() const override{return re_;}; //... other functions private: double re_; double im_; // other }; }; namespace polar{ class complex{ /// polar complex numbers.... }; };
我希望能在main函数中编写如下代码:
cartesian::complex c(1,2); polar::complex p(3,PI); exponential::complex q = exp(c) + p; print(q)
但问题在于,我需要为每个类编写大量带前向声明的赋值运算符。请问是否有办法让复数类的所有表示形式为同一类型,仅通过不同构造器区分?比如使用pimpl模式搭配不同实现指针,或是借助模板实现?
解决方案
方法一:Pimpl模式+多态实现
核心思路是用一个统一的Complex类作为对外接口,内部用智能指针指向不同的实现类(笛卡尔、极坐标、指数形式),通过标签分派区分不同构造逻辑,把多态细节完全封装在内部。
实现示例
#include <memory> #include <iostream> #include <cmath> // 前向声明实现基类 class ComplexImpl; class Complex { public: // 标签类型:区分不同构造方式 struct CartesianTag {}; struct PolarTag {}; struct ExponentialTag {}; // 笛卡尔坐标构造 Complex(double re, double im); // 极坐标构造(通过标签避免参数歧义) Complex(double r, double theta, PolarTag); // 指数形式构造(复用极坐标实现逻辑) Complex(double r, double theta, ExponentialTag); // 运算符与功能方法 Complex operator+(const Complex& other) const; Complex exp() const; void print() const; // 访问器 double re() const; double im() const; // 自动管理拷贝/移动语义 ~Complex(); Complex(const Complex& other); Complex& operator=(const Complex& other); Complex(Complex&& other) noexcept; Complex& operator=(Complex&& other) noexcept; private: std::unique_ptr<ComplexImpl> pimpl_; }; // 抽象实现基类 class ComplexImpl { public: virtual ~ComplexImpl() = default; virtual std::unique_ptr<ComplexImpl> clone() const = 0; virtual std::unique_ptr<ComplexImpl> add(const ComplexImpl& other) const = 0; virtual std::unique_ptr<ComplexImpl> exp() const = 0; virtual double re() const = 0; virtual double im() const = 0; virtual void print() const = 0; }; // 笛卡尔坐标实现类 class CartesianImpl : public ComplexImpl { public: CartesianImpl(double re, double im) : re_(re), im_(im) {} std::unique_ptr<ComplexImpl> clone() const override { return std::make_unique<CartesianImpl>(re_, im_); } std::unique_ptr<ComplexImpl> add(const ComplexImpl& other) const override { return std::make_unique<CartesianImpl>(re_ + other.re(), im_ + other.im()); } std::unique_ptr<ComplexImpl> exp() const override { double r = std::exp(re_); return std::make_unique<CartesianImpl>(r * std::cos(im_), r * std::sin(im_)); } double re() const override { return re_; } double im() const override { return im_; } void print() const override { std::cout << re_ << " + " << im_ << "i" << std::endl; } private: double re_, im_; }; // 极坐标实现类 class PolarImpl : public ComplexImpl { public: PolarImpl(double r, double theta) : r_(r), theta_(theta) {} std::unique_ptr<ComplexImpl> clone() const override { return std::make_unique<PolarImpl>(r_, theta_); } std::unique_ptr<ComplexImpl> add(const ComplexImpl& other) const override { // 转笛卡尔坐标后再相加 double re = r_ * std::cos(theta_) + other.re(); double im = r_ * std::sin(theta_) + other.im(); return std::make_unique<CartesianImpl>(re, im); } std::unique_ptr<ComplexImpl> exp() const override { double new_r = std::exp(r_ * std::cos(theta_)); double new_theta = r_ * std::sin(theta_) + theta_; return std::make_unique<PolarImpl>(new_r, new_theta); } double re() const override { return r_ * std::cos(theta_); } double im() const override { return r_ * std::sin(theta_); } void print() const override { std::cout << r_ << " * e^(i*" << theta_ << ")" << std::endl; } private: double r_, theta_; }; // Complex类构造器实现 Complex::Complex(double re, double im) : pimpl_(std::make_unique<CartesianImpl>(re, im)) {} Complex::Complex(double r, double theta, Complex::PolarTag) : pimpl_(std::make_unique<PolarImpl>(r, theta)) {} Complex::Complex(double r, double theta, Complex::ExponentialTag) : pimpl_(std::make_unique<PolarImpl>(r, theta)) {} // 运算符与方法转发到内部实现 Complex Complex::operator+(const Complex& other) const { Complex result(0, 0); result.pimpl_ = pimpl_->add(*other.pimpl_); return result; } Complex Complex::exp() const { Complex result(0, 0); result.pimpl_ = pimpl_->exp(); return result; } void Complex::print() const { pimpl_->print(); } double Complex::re() const { return pimpl_->re(); } double Complex::im() const { return pimpl_->im(); } // 拷贝/移动语义实现 Complex::Complex(const Complex& other) : pimpl_(other.pimpl_->clone()) {} Complex& Complex::operator=(const Complex& other) { if (this != &other) { pimpl_ = other.pimpl_->clone(); } return *this; } Complex::Complex(Complex&& other) noexcept = default; Complex& Complex::operator=(Complex&& other) noexcept = default; Complex::~Complex() = default;
对应main函数用法
int main() { const double PI = std::acos(-1.0); // 笛卡尔坐标初始化 Complex c(1, 2); // 极坐标初始化(通过标签区分) Complex p(3, PI, Complex::PolarTag{}); // 指数形式初始化+运算 Complex q = c.exp() + p; q.print(); return 0; }
这种方案下,所有复数都是统一的Complex类型,内部自动处理不同表示形式的转换,无需为每个子类编写重复的赋值运算符。
方法二:模板+标签分派(编译期多态)
如果不需要运行时切换复数表示形式,可采用模板结合std::variant实现编译期多态,避免动态分配的开销。
实现示例
#include <variant> #include <iostream> #include <cmath> // 标签类型 struct CartesianTag {}; struct PolarTag {}; struct ExponentialTag {}; // 模板实现类 template<typename Tag> class ComplexImpl; // 笛卡尔坐标特化 template<> class ComplexImpl<CartesianTag> { public: ComplexImpl(double re, double im) : re_(re), im_(im) {} double re() const { return re_; } double im() const { return im_; } ComplexImpl<CartesianTag> operator+(const auto& other) const { return {re_ + other.re(), im_ + other.im()}; } ComplexImpl<CartesianTag> exp() const { double r = std::exp(re_); return {r * std::cos(im_), r * std::sin(im_)}; } void print() const { std::cout << re_ << " + " << im_ << "i" << std::endl; } private: double re_, im_; }; // 极坐标特化 template<> class ComplexImpl<PolarTag> { public: ComplexImpl(double r, double theta) : r_(r), theta_(theta) {} double re() const { return r_ * std::cos(theta_); } double im() const { return r_ * std::sin(theta_); } ComplexImpl<CartesianTag> operator+(const auto& other) const { return {re() + other.re(), im() + other.im()}; } ComplexImpl<PolarTag> exp() const { double new_r = std::exp(r_ * std::cos(theta_)); double new_theta = r_ * std::sin(theta_) + theta_; return {new_r, new_theta}; } void print() const { std::cout << r_ << " * e^(i*" << theta_ << ")" << std::endl; } private: double r_, theta_; }; // 统一对外的Complex类 class Complex { public: // 笛卡尔构造 Complex(double re, double im) : impl_(ComplexImpl<CartesianTag>{re, im}) {} // 极坐标构造 Complex(double r, double theta, PolarTag) : impl_(ComplexImpl<PolarTag>{r, theta}) {} // 指数构造 Complex(double r, double theta, ExponentialTag) : impl_(ComplexImpl<PolarTag>{r, theta}) {} // 加法运算 Complex operator+(const Complex& other) const { auto result = std::visit([](const auto& a, const auto& b) { return a + b; }, impl_, other.impl_); return Complex(result.re(), result.im()); } // 指数运算 Complex exp() const { auto result = std::visit([](const auto& impl) { return impl.exp(); }, impl_); return Complex(result.re(), result.im()); } void print() const { std::visit([](const auto& impl) { impl.print(); }, impl_); } double re() const { return std::visit([](const auto& impl) { return impl.re(); }, impl_); } double im() const { return std::visit([](const auto& impl) { return impl.im(); }, impl_); } private: std::variant<ComplexImpl<CartesianTag>, ComplexImpl<PolarTag>> impl_; };
main函数用法与之前一致
这种方案利用编译期多态提升性能,同时保持对外接口的统一性。
总结
- 若需运行时切换复数表示形式,优先选择Pimpl+多态方案,封装性强,符合开闭原则。
- 若无需运行时切换、追求性能,选择模板+标签分派+variant方案,避免动态分配开销。
两种方案均能实现统一的复数类型,通过不同构造逻辑初始化不同表示形式,彻底避免重复编写赋值运算符的问题。
内容的提问来源于stack exchange,提问作者Eric Cardozo
相关产品推荐
相关产品推荐

