我有一个下面定义的类:

class CVariable
{
   public:
     CVariable(CString strData, int nNum);
     CVariable(BSTR bsData);
     ~CVariable();
   public:
     VARIANT GetVariant(){return m_bsVa;};
   private:
     VARIANT m_bsVa;
   VARIANT m_nVa;
};


和工具是:

CVariable::CVariable(CString strData, int nNum)
{
  VariantInit(&m_bsVa);
  BSTR bsData = ::SysAllocString(strData);
  m_bsVa.vt = VT_BSTR;
  m_bsVa.bstrVal = bsData;
  ::SysFreeString(bsData);

  VariantInit(&m_nVa);
  m_nVa.vt = VT_I2;
  m_nVa.lVal = nNum;
}

CVariable::CVariable(BSTR bsData) {
  m_bsVa.vt = VT_BSTR;
  m_bsVa.bstrVal = bsData;
}

CVariable::~CVariable()
{
  VariantClear(&m_bsVa);
  VariantClear(&m_nVa);
}


当我尝试使用构造函数CVariable(CString,int)构造两个实例时,
类成员m_bsVas始终具有相同的值,而m_nVas不同。结果如下:
c++ - 具有VARIANT成员的C++类对象具有奇怪的行为-LMLPHP

如您所见,v1和v2具有相同的m_bsVa但具有不同的m_nVa,而使用构造函数CVariable(BSTR)则得到正确的结果。我不知道为什么会这样?
任何帮助将不胜感激。

最佳答案

我发现您的代码有几个问题。


CVariable(CString, int)构造函数为BSTR分配m_bsVa,但随后立即释放BSTR,使m_bsVa指向无效的内存,并允许下一个CVariable实例为其使用潜在的重用相同的内存地址已分配BSTR。您需要保留分配给BSTR的权限,直到使用完m_bsVa(或者至少要为其分配新的值)为止。 VariantClear()将为您释放BSTR
CVariable(BSTR)构造函数根本没有初始化m_nVa,这将对其后续的操作(包括VariantClear())造成问题。同样,构造函数正在获取调用方BSTR的所有权。取决于您如何使用此构造函数,这可能可行,也可能不可行。如果呼叫者不希望您获得所有权,则需要使用BSTR制作SysAllocString/Len()的副本。
VARIANT不可复制。您需要使用VariantCopy()函数将数据从一个VARIANT复制到另一个。这意味着您的CVariable类需要实现一个复制构造函数和一个复制赋值运算符。无论如何,您都需要执行该操作,以便您的类符合Rule of Three
GetVariant()按原样返回m_bsVa,因此编译器将按原样复制m_bsVa字段的值到调用方的接收方VARIANT中。由于BSTR是指针,因此调用者将可以直接访问类内部的原始BSTR。取决于您使用GetVariant()的方式,这可能会也可能不会。在当前实现中,对返回的BSTR的任何访问都应视为只读-调用方不得在其上调用SysFreeString(),并且必须期望对CVariable对象的任何更改都可能使BSTR无效。如果那不符合您的需求,则GetVariant()应该返回一个新的VARIANT,该新的VariantCopy()已通过VariantClear()复制了数据,然后调用者可以在使用返回的VARIANT完成后调用variant_t


话虽如此,请尝试类似以下的操作:

class CVariable
{
public:
    CVariable(const CString &strData, int nNum);
    CVariable(BSTR bsData);
    CVariable(const CVariable &src);
    ~CVariable();

    VARIANT GetVariant() const;

    CVariable& operator=(const CVariable &src);
    CVariable& operator=(BSTR src);

private:
    VARIANT m_bsVa;
    VARIANT m_nVa;
};




CVariable::CVariable(const CString &strData, int nNum)
{
    ::VariantInit(&m_bsVa);
    m_bsVa.vt = VT_BSTR;
    m_bsVa.bstrVal = ::SysAllocString(strData);

    ::VariantInit(&m_nVa);
    m_nVa.vt = VT_I2;
    m_nVa.lVal = nNum;
}

CVariable::CVariable(BSTR bsData)
{
    ::VariantInit(&m_bsVa);
    m_bsVa.vt = VT_BSTR;
    m_bsVa.bstrVal = bsData;
    /* or this, if needed:
    m_bsVa.bstrVal = ::SysAllocStringLen(bsData, ::SysStringLen(bsData));
    */

    ::VariantInit(&m_nVa);
}

CVariable::~CVariable()
{
    ::VariantClear(&m_bsVa);
    ::VariantClear(&m_nVa);
}

VARIANT CVariable::GetVariant() const
{
    return m_bsVa;
    /* or this, if needed:
    VARIANT result;
    ::VariantInit(&result);
    ::VariantCopy(&result, &m_bsVa);
    return result;
    */
}

CVariable& CVariable::operator=(const CVariable &src)
{
    if (&src != this)
    {
        ::VariantClear(&m_bsVa);
        ::VariantCopy(&m_bsVa, &src.m_bsVa);

        ::VariantClear(&m_nVa);
        ::VariantCopy(&m_nVa, &src.m_nVa);
    }

    return *this;
}

CVariable& CVariable::operator=(BSTR src)
{
    ::VariantClear(&m_bsVa);
    m_bsVa.vt = VT_BSTR;
    m_bsVa.bstrVal = src;
    /* or this, if needed:
    m_bsVa.bstrVal = ::SysAllocStringLen(src, ::SysStringLen(src));
    */

    ::VariantClear(&m_nVa);

    return *this;
}


如果直接使用VARIANT类而不是,则可以大大简化代码,同时仍能解决上述所有要点:

class CVariable
{
public:
    CVariable(const CString &strData, int nNum);
    CVariable(BSTR bsData);

    variant_t GetVariant() const;

private:
    variant_t m_bsVa;
    variant_t m_nVa;
};




CVariable::CVariable(const CString &strData, int nNum)
    : m_bsVa(strData), m_nVa(nNum)
{
}

CVariable::CVariable(BSTR bsData)
    : m_bsVa(bsData)
{
}

variant_t CVariable::GetVariant() const
{
    return m_bsVa;
}

关于c++ - 具有VARIANT成员的C++类对象具有奇怪的行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40143927/

10-17 01:46