请考虑以下代码:
// A non-copyable, non-movable aggregate
struct Strange
{
const int & i;
char & c;
};
class Container
{
private:
int my_i;
char my_c;
Strange thing;
public:
// Valid, because both `my_i´ and `my_c´ are non-const
// objects to which both references can be bound.
explicit
Container
( )
noexcept
: thing{ my_i , my_c }
{ }
// How could this be implemented?
auto &
operator=
( const Container & that )
noexcept
{
this->my_i = that->my_i;
this->my_c = that->my_c;
// What to do with `thing´?
return *this;
}
};
动态分配Strange
对象
class Container
{
private:
int my_i;
char my_c;
Strange * thing;
public:
// Note that it isn't exception safe.
explicit
Container
( )
: thing(new Strange{ my_i , my_c })
{ }
auto &
operator=
( const Container & that )
noexcept
{
this->my_i = that->my_i;
this->my_c = that->my_c;
delete this->thing;
this->thing = new Strange { this->my_i , this->my_c };
return *this;
}
};
关注:
危险:必须非常小心,不要泄漏记忆。
除了使代码更具可读性之外,使用智能指针(即std::unique_ptr
)只会解决最后一点。
使用展示位置
class Container
{
private:
int my_i;
char my_c;
Strange thing;
public:
explicit
Container
( )
noexcept
: thing{ my_i , my_c }
{ }
auto &
operator=
( const Container & that )
noexcept
{
this->my_i = that.my_i;
this->my_c = that.my_c;
// Placement new is exception safe, and so is
// construction of `Strange´.
this->thing.~Strange();
new(&this->thing) Strange { this->my_i , this->my_c };
return *this;
}
};
关注:
Strange
的析构函数会释放thing
占用的内存吗?
我认为,就像构造函数一样,析构函数不负责内存管理。而且,我的代码似乎工作正常。但是,我想澄清一下。
内存对齐怎么样?
我的猜测是,由于它取代了相同类型的现有对象,因此内存已经对齐。这是对的吗?
Container
的析构函数是否会破坏thing
?
除了证实和/或驳斥我上面解释的问题之外,我想知道是否还有其他选择。如果是,请举例说明。
这个问题出现在一个应该提供类似于std::unordered_map
的界面的类上。我的类不是重新实现它,而是封装了这样的容器,只是充当了大多数方法的代理:它的迭代器包含了map提供的那些,它的对是一个聚合结构,具有适当命名的成员(是对它的引用)实际数据),在Strange
提供的示例中表示。由于迭代器需要返回对实际数据的引用和指针,因此我的自定义迭代器包含一对。问题是修改它(当递增或分配迭代器时)。我承认这可能不是一个好主意,并且那些 会影响性能,但无论如何我对此事感兴趣。
我刚刚意识到,不是从我的自定义迭代器返回指向指向实际数据(封装映射的数据)的成员自定义对的引用和指针,而是可以返回就地构造的自定义对(即{{ 1}}对象)。通常,我们没有看到我们在一个山洞里,而不是退出它,继续前进:)。请原谅我的噪音,我将问题标记为"已关闭"。
答案 0 :(得分:1)
(如果我们正在谈论移动对象并使用auto
关键字,则应在问题中添加c++11
标记。
我不确定我真的理解你的问题;你给我的例子似乎没有想到我;在Strange
中使用指针会好得多。
例如,这编译并且工作得非常好,并且在功能上等同于我想要做的事情。
struct Strange
{
Strange()
: i(nullptr), c(nullptr) {}
Strange( const int *_i, const char *_c )
: i(_i), c(_c) {}
const int *i;
const char *c;
};
class Container
{
int my_i;
char my_c;
Strange thing;
public:
Container()
: thing(&my_i,&my_c)
{ }
Container( int i, char c )
: my_i(i), my_c(c), thing(&my_i,&my_c)
{ }
Container( int i, char c, const Strange& s )
: my_i(i), my_c(c), thing(s) // use default copy-constructor
{ }
Container &
operator=
( const Container & that )
{
my_i = that.my_i;
my_c = that.my_c;
thing = that.thing;
return *this;
}
};
int main()
{
Container a(12,24);
Container b(25,42);
b = a;
}
请注意,引用对象内存通常很危险。
例如,在此使用memcpy
将是一种灾难。
(用clang和g ++编译)