2014-04-30 59 views
20

我正在使用一个库,它具有与其构造函数不同的init函数。我每次做一个新的实例我需要调用,例如:从现在开始,我可以创建一个变量_const吗?

MyClass a; 
a.init(); 

由于initconst,这使我从创建const的情况下(我不能写const MyClass a)。有什么办法可以调用init,然后从“here on out”声明(我猜想其余的范围)我的变量是const

这工作,而是依赖于不接触原始变量:

MyClass dont_touch; 
dont_touch.init(); 
const MyClass & a = dont_touch; 
+0

答案是否定的。像你一样,我希望是的。 – Mehrdad

+0

你也可以使用'const_cast'作为'init'调用来做到快速和肮脏。也就是说,在'init'调用的对象周围声明对象'const'和'const_cast'。然而,'const_cast'通常被认为是不好的做法,我必须说我认为[模板解决方案](http://stackoverflow.com/a/23400028/3100771)是超级光滑的。 – Apriori

+1

@Apriori绝对未定义的行为。不能const_cast从一个已定义的const对象中取出const并且对它做非const的东西。 –

回答

16

如果您使用C++ 11,你可以使用lambda函数

const MyClass ConstantVal = []{ 
    MyClass a; 
    a.init(); 
    return a; 
}(); 

这可以让你保持初始化到位而永不放弃的可变对象的外部访问。 还看到: http://herbsutter.com/2013/04/05/complex-initialization-for-a-const-variable/

+1

我认为这比我的解决方案更优化,因为编译器可以假定对象从未被修改过。 –

+0

这回答我的具体问题。尽管我可能会问一个更普遍的问题,其中'a.init()'被替换为一些可能不适合lambda的任意操作。 –

5

创建一个包装的前两行,给你的对象是准备去的功能。

MyClass makeMyClass() 
{ 
    MyClass a; 
    a.init(); 
    return a; 
} 

// Now you can construct a const object or non-const object. 
const MyClass a = makeMyClass(); 
MyClass b = makeMyClass(); 

更新

使用makeMyClass()涉及临时对象每次被调用的函数的构造和破坏。如果变成显著成本,makeMyClass()可以改变:

MyClass const& makeMyClass() 
{ 
    static bool inited = false; 
    static MyClass a; 
    if (!inited) 
    { 
    inited = true; 
    a.init(); 
    } 
    return a; 
} 

它的使用,如前文所述,将继续工作。另外,一次也可以这样做:

const MyClass& c = makeMyClass(); 
+0

这是以复制为代价的,是正确的? –

+1

是的,它确实是以牺牲拷贝为代价的。 –

+3

@mangledorf我不确定这一点,但[返回值优化](https://en.wikipedia.org/wiki/Return_value_optimization)可能会有用。所以,如果我理解正确的话,如果你使用'-O3',我不认为你需要担心这个。只是一个想法。 – gongzhitaao

9

您可以创建一个包装类并使用它。

如果MyClass的有虚析构函数,你可以从中感到安全推导如下:

class WrapperClass : public MyClass 
{ 
public: 
    WrapperClass() 
    { 
     init(); // Let's hope this function doesn't throw 
    } 
}; 

,或者编写包含MyClass的实例

class WrapperClass 
{ 
public: 
    WrapperClass() 
    { 
     m_myClass.init(); // Let's hope this function doesn't throw 
    } 
    operator MyClass&() {return m_myClass;} 
    operator const MyClass&() const {return m_myClass;} 
private: 
    MyClass m_myClass; 
}; 

类或编写模板来解决这个使用上述两种解决方案之一的一般问题:例如,

template <class T> class WrapperClass : public T 
{ 
public: 
    WrapperClass() 
    { 
     T::init(); 
    } 
}; 

typedef WrapperClass<MyClass> WrapperClass; 
+0

可能值得注意的是,如果需要,可以在其使用的函数内部和局部定义这样的派生类。但使用模板解决方案更好,避免这种情况,因为您可以随意实例化。 – Apriori

+1

'init'完全没问题。我们希望它不会返回错误代码。 init函数的通常原因是开发人员知道可能发生错误,但不知道异常,所以如果发生错误,他不能报告错误。 – MSalters

1

实际上,你可以做到这一点很简单,即使没有C++ 11 lambda表达式和:

const MyClass a; 
{ 
    MyClass _a; 
    _a.init(); 
    std::swap(const_cast<MyClass&>(a), _a); 
} 

采用const_cast是无可否认的一个黑客位,但它赢得了” t破坏任何东西const是一个相当弱的说明符。同时,它非常高效,因为MyClass对象只能被交换,不能被复制(最合理的复制对象应该提供swap函数并注入过载std::swap)。

不投,它需要一个帮手:

struct Construct_Init { 
    operator MyClass() const 
    { 
     MyClass a; 
     a.init(); 
     return a; 
    } 
}; 
const MyClass a = Construct_Init(); 

这可就是这样一个功能(Construct_Init结构需要没有命名空间范围进行声明),但它是一个有点长。对象的副本可能会或可能不会使用copy elision优化。

请注意,在这两种情况下,返回值init()都会丢失。如果它返回一个布尔值,其中true是成功和false是失败的,最好是:

if(!a.init()) 
    throw std::runtime_error("MyClass init failed"); 

或者只是确保妥善处理错误。

相关问题