You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:05:30