我有一个定义如下的课程:
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_bsVa 总是具有相同的值,而 m_nVa 是不同的。结果如下:
如您所见, v1 和 v2 具有相同的m_bsVa但m_nVa不同,而使用构造函数CVariable(BSTR)
会导致正确的结果。我不知道为什么会发生这种情况?
任何帮助将不胜感激。
答案 0 :(得分:1)
我发现您的代码存在一些问题。
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
。这意味着您的GetVariant()
类需要实现复制构造函数和复制赋值运算符。无论如何你需要做什么才能使你的班级符合Rule of Three。
m_bsVa
按原样返回m_bsVa
,因此编译器只会将VARIANT
的字段的值原样复制到调用者的接收{{1}中}。由于BSTR
是指针,因此调用者将直接访问到您班级内的原始BSTR
。根据您使用GetVariant()
的方式,这可能会也可能不会。在当前实现中,对返回的BSTR
的任何访问都应被视为只读 - 调用者不得在其上调用SysFreeString()
,并且必须预期对其进行任何更改。 CVariable
对象可能使BSTR
无效。如果这不符合您的需求,那么GetVariant()
应该返回通过VARIANT
复制了数据的新VariantCopy()
,然后调用者可以在完成后使用VariantClear()
调用VARIANT
已退回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
如果直接使用variant_t
类而不是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;
}
ProcessBuilder
答案 1 :(得分:0)
在这个构造函数中:
CVariable::CVariable(BSTR bsData) {
m_bsVa.vt = VT_BSTR;
m_bsVa.bstrVal = bsData;
}
您要离开m_nVa
未初始化 - 它会获得一些随机值。它应该是这样的:
CVariable::CVariable(BSTR bsData) {
VariantInit(&m_bsVa);
m_bsVa.vt = VT_BSTR;
m_bsVa.bstrVal = bsData;
VariantInit(&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;
}
请勿致电::SysFreeString(bsData);
,因为bsData
归m_bsVa
所有。
SysFreeString()
释放内存,下一个SysAllocString()
调用可能会在同一内存地址创建一个新的BSTR
字符串。
我建议您改用_variant_t
class,而不是使用裸 VARIANT。在这种情况下,您根本不需要担心VariantInit()
/ VariantClear()
,因为它为您实现了C ++风格的所有权策略。
答案 2 :(得分:0)
我建议你在原始C VARIANT周围使用一个方便的C ++ RAII包装器,比如来自ATL的CComVariant。
这将简化您的代码,因为CComVariant将正确初始化其包装的原始VARIANT,并将其清理。
您可以使用更安全的CComVariant包装替换VARIANT数据成员:
CComVariant m_bsVa;
CComVariant m_nVa;
然后你可以在这样的构造函数中初始化它们:
CVariable::CVariable(const CString& strData, int nNum)
: m_bsVa(strData), m_nVa(nNum)
{}
CVariable::CVariable(BSTR bsData)
: m_bsVa(bsData)
{}
请注意,您不需要显式定义析构函数,因为在这种情况下,CComVariant的析构函数将正确清理数据成员。
你的getter可以像这样实现:
const CComVariant& CVariable::GetVariant() const
{
return m_bsVa;
}