对象似乎没有在 C++ 中通过引用传递

Objects seemingly not being passed by reference in C++

编辑:已发现问题是包装器 class 在传递到 MCEngine 函数和模拟引擎 class 中时正在创建 BSCallFunction 的副本,如下所示。考虑到这一点,我仍然需要弄清楚如何让程序按照我的意愿去做,如问题中所述。评论部分包含到目前为止的发现。

class SimulationEngine
{
public:
    SimulationEngine(double horizon, const Wrapper<valuationFunction>& theFunction_, RiskFactor simulatedRiskFactor);
    virtual void DoOnePath(double vol, double normvariate) = 0;
    virtual SimulationEngine* clone() const = 0;
    const double GetHorizon();
    Wrapper<valuationFunction>& GetFunction();
    RiskFactor simulatedRiskFactor;
protected:
    double horizon;
    Wrapper<valuationFunction> theFunction;
};

在我的主 class 中,我正在创建这个对象:

    BSCallFunction ThisBsCall(nominal, S0, r, d, impvol, TTM, Strike);

我将其传递给两个 classes(派生自相同的基 class),这将改变它的一些值(如 S0 和 r)。

    OneStepBSEngine BSSimulation(timeHorizon, zeroDrift, ThisBsCall, RiskFactor::equity);
    OneStepBrownianMotionEngine ShortRateSimulation(timeHorizon, zeroDrift, ThisBsCall, RiskFactor::interest_rate);

我希望两个 classes 都更改同一个对象的值,这样我最终得到一个具有不同 r 和 S0 的 ThisBsCall。

classes 具有以下结构:

class OneStepBrownianMotionEngine : public SimulationEngine
{
public:
    OneStepBrownianMotionEngine(double horizon_, double drift_,const Wrapper<valuationFunction>& theFunction_, RiskFactor simulatedRiskFactor_);
    virtual SimulationEngine* clone() const;
    void DoOnePath(double vol, double normvariate);
private:
    double drift;
};

OneStepBrownianMotionEngine::OneStepBrownianMotionEngine(double horizon_, double drift_,const Wrapper<valuationFunction>& theFunction_, RiskFactor simulatedRiskFactor_) : SimulationEngine(horizon_, theFunction_, simulatedRiskFactor_), drift(drift_)
{
}

我通过 const 引用传递 "ThisBsCall" 对象的地方。

回到我的主 class 然后我将创建对象 MCEngine 并使用将使用上面的引擎 classes 更改 r 和 S0 值的函数,Enginevector 只是一个两个引擎的向量 classes:

MCEngine VAREngine(EngineVector, covmat);
VAREngine.DoSimulation(gathererCombiner, NumberOfPaths);

我也像这样通过 const 引用传递 enginevector:

MCEngine(const std::vector<Wrapper<SimulationEngine>>& EngineVector, std::vector<std::vector<double>> covMatrix_);

我不确定这是否重要,但在 MCEngine 中使用的构造函数和函数看起来像这样 class:

MCEngine::MCEngine(const std::vector< Wrapper<SimulationEngine>>& EngineVector_, std::vector<std::vector<double>> covMatrix_)
    : cholMatrix(Cholesky_Decomposition(covMatrix_)), EngineVector(EngineVector_), V(0)
{
}

void MCEngine::DoSimulation(StatisticsMC& TheGatherer, unsigned long NumberOfPaths)
{
    UpdateTTM();  
    double thisPortfolioValue;
    for (unsigned long i = 0; i < NumberOfPaths; ++i)
    {
        std::vector<Wrapper<SimulationEngine>> tmpEngineVector(EngineVector);  //save the origianl EngineVector to use on next path
        //create vector of correlated normal variats with cholezky decompositon
        MJArray corrNormVariates(cholMatrix.size());
        corrNormVariates = 0;
        MJArray NormVariates(cholMatrix.size());
        for (unsigned long i = 0; i < cholMatrix.size(); i++)
        {
            NormVariates[i] = GetOneGaussianByBoxMuller();
            for (unsigned long j = 0; j < cholMatrix[i].size(); j++) {
                corrNormVariates[i] += cholMatrix[i][j] * NormVariates[j];
            }
            corrNormVariates[i] /= cholMatrix[i][i]; //normalize the random variates
        }
        //use each one for simulation with spotvector/volvector
        for (unsigned long j = 0; j < EngineVector.size(); ++j)
        {
            EngineVector[j]->DoOnePath(cholMatrix[j][j], corrNormVariates[j]); //updates the riskfactors for the positions
        }
        ValuePortfolio();
        thisPortfolioValue = GetPortfolioValue();
        TheGatherer.DumpOneResult(thisPortfolioValue);
        EngineVector = tmpEngineVector;
    }
}

现在我的问题是,当我查看 2 个 BSCallFunction 对象(我希望它们是相同的 ThisBsCall)时,它们具有不同的 S0 和 r 值,所以似乎在某些时候我复制了对象并更改了此副本的 r 或 S0 的值代替原始值,然后返回结果。是否可以从我 post 编辑的内容(或关于我可能做错了什么的一般性建议)中看出这可能发生在哪里?如果有帮助,我可以 post 更多代码。

编辑:包装器 class 以防万一这可能是导致问题的原因:

#ifndef WRAPPER_H
#define WRAPPER_H
template< class T>
class Wrapper
{
public:
    Wrapper()
    {
        DataPtr = 0;
    }
    Wrapper(const T& inner)
    {
        DataPtr = inner.clone();
    }
    Wrapper(T* DataPtr_)
    {
        DataPtr = DataPtr_;
    }
    ~Wrapper()
    {
        if (DataPtr != 0)
            delete DataPtr;
    }
    Wrapper(const Wrapper<T>& original)
    {
        if (original.DataPtr != 0)
            DataPtr = original.DataPtr->clone();
        else
            DataPtr = 0;
    }
    Wrapper& operator=(const Wrapper<T>& original)
    {
    if (this != &original)
    {
    T* newPtr = (original.DataPtr != 0) ?
    original.DataPtr->clone() : 0;
    if (DataPtr != 0)
    delete DataPtr;
    DataPtr = newPtr;
    }
    return *this;
    }

        T& operator*()
    {
        return *DataPtr;
    }
    const T& operator*() const
    {
        return *DataPtr;
    }
    const T* const operator->() const
    {
        return DataPtr;
    }
    T* operator->()
    {
        return DataPtr;
    }
private:
    T* DataPtr;
};
#endif

您认为 Wrapper class 正在创建 BSCallFunction 对象的多个副本是正确的。

例如:

BSCallFunction ThisBsCall(nominal, S0, r, d, impvol, TTM, Strike);
OneStepBrownianMotionEngine ShortRateSimulation(timeHorizon, zeroDrift, ThisBsCall, RiskFactor::interest_rate);

在上面的行中,创建了一个临时 Wrapper 对象并将其传递给 OneStepBrownianMotionEngine 的构造函数,该构造函数克隆了内部对象并存储了指向它的指针。然后这个克隆的对象被传递给基础 class SimulationEngine,它使用 Wrapper class.

的复制构造函数存储它的副本

所以 1 个临时对象 + 另一个副本。

另一个引擎也会发生这种情况 class OneStepBrownianMotionEngine.

Now my problem is when I look at the 2 BSCallFunction objects (which I expect to be the same ThisBsCall) they have different values for S0 and r, so it seems like at some point I copied the object and changed the value of r or S0 of this copy instead of the original and then returned the result.

它们永远不会相同,因为 Wrapper class 旨在克隆传递给它的对象。您可以在堆 (new BSCallFunction(...)) 上创建 ThisBsCall 并使用指针。这将确保两个引擎使用相同的 ThisBsCall

但是,正如您所发现的,这会在引擎被销毁时导致问题:Wrapper 的析构函数试图删除相同的 ThisBsCall 对象。双重删除总是会导致崩溃!

你的 Wrapper 是什么:

  • 使用指向 ThisBsCall 的指针,这样您就不会复制它
  • Wrapper 对象的每个共享所有者维护一个引用计数
  • 当引用计数降为零时销毁 ThisBsCall 对象

std::shared_ptr 为您完成这一切,它是标准库的一部分。所以我建议你使用这个而不是你的 Wrapper class。它将需要最少的更改。

除了替换

的实例外,您的代码可以保持不变
Wrapper<your_class_name>

std::shared_ptr<your_class_name> 

例如:

std::shared_ptr<valuationFunction> ThisBsCall = std::make_shared<BSCallFunction>(nominal, S0, r, d, impvol, TTM, Strike);

smart pointersstd::shared_ptrstd::unique_ptr 的美妙之处在于它们会为您处理内存管理。您不必担心手动调用 newdelete。所有这一切都在后台为您完成。另外,它们也能为您提供卓越的安全保障。如果由于某种原因在内存分配期间抛出异常,则会为您清理内存。由于这些原因以及更多原因,这就是为什么现在推荐的做法是使用智能指针而不是我们自己进行内存管理。

不知道你是不是在多线程环境下写代码?尽管存储托管对象和引用计数器的控制块是线程安全的,但智能指针本身却不是。您仍然需要提供自己的线程同步。