如果operator=正确定义,可以使用以下作为复制构造函数吗?
MyClass::MyClass(MyClass const &_copy)
{
*this = _copy;
}
Run Code Online (Sandbox Code Playgroud) 我正在修改我的C++,我正在处理运算符重载,特别是"="(赋值)运算符.我在网上看到并且遇到了讨论它的多个主题.在我自己的笔记中,我把所有的例子都记下来了
class Foo
{
public:
int x;
int y;
void operator=(const Foo&);
};
void Foo::operator=(const Foo &rhs)
{
x = rhs.x;
y = rhs.y;
}
Run Code Online (Sandbox Code Playgroud)
在我在网上找到的所有参考文献中,我注意到操作符返回对源对象的引用.为什么返回对象的引用的正确方法而不是什么都没有?
struct Foo
{
Foo(int i)
{
ptr = new int(i);
}
~Foo()
{
delete ptr;
}
int* ptr;
};
int main()
{
{
Foo a(8);
Foo b(7);
a = b;
}
//Do other stuff
}
Run Code Online (Sandbox Code Playgroud)
如果我理解正确,编译器将自动为其创建赋值运算符成员函数Foo.但是,这只需要输入ptrin 的值b并将其放入a.a最初分配的内存似乎丢失了.我可以a.~Foo();在进行赋值之前进行调用,但我听说你应该很少需要显式调用析构函数.所以让我们说我编写一个赋值运算符Foo,删除int左操作数的指针,然后将r值赋给l值.像这样:
Foo& operator=(const Foo& other)
{
//To handle self-assignment:
if (this != &other) {
delete this->ptr;
this->ptr = other.ptr;
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我这样做,那么当Foo a和Foo …
请考虑以下代码:
#include <algorithm>
#include <iostream>
#include <vector>
namespace my_space
{
struct A
{
double a;
double* b;
bool operator<(const A& rhs) const
{
return this->a < rhs.a;
}
};
void swap(A& lhs, A& rhs)
{
std::cerr << "My swap.\n";
std::swap(lhs.a, rhs.a);
std::swap(lhs.b, rhs.b);
}
}
int main()
{
const int n = 20;
std::vector<my_space::A> vec(n);
for (int i = 0; i < n; ++i) {
vec[i].a = -i;
}
for (int i = 0; i < n; ++i) {
std::cerr …Run Code Online (Sandbox Code Playgroud) 我已经看到了在各个地方推荐的复制和交换习惯用法,作为为赋值运算符实现强异常安全性的推荐/最佳/唯一方法.在我看来,这种方法也有缺点.
考虑以下简化的类似矢量的类,它使用复制和交换:
class IntVec {
size_t size;
int* vec;
public:
IntVec()
: size(0),
vec(0)
{}
IntVec(IntVec const& other)
: size(other.size),
vec(size? new int[size] : 0)
{
std::copy(other.vec, other.vec + size, vec);
}
void swap(IntVec& other) {
using std::swap;
swap(size, other.size);
swap(vec, other.vec);
}
IntVec& operator=(IntVec that) {
swap(that);
return *this;
}
//~IntVec() and other functions ...
}
Run Code Online (Sandbox Code Playgroud)
通过复制构造函数实现赋值可能是有效的并且可以保证异常安全,但是它也可能导致不必要的分配,甚至可能导致无内存错误.
考虑分配700MB的情况下IntVec,以一个1GB IntVec以<2GB堆限制的机器上.最佳分配将意识到它已经分配了足够的内存,并且只将数据复制到已经分配的缓冲区中.复制和交换实现将导致在释放1GB缓冲区之前分配另一个700MB缓冲区,导致所有3个缓冲区同时尝试在内存中共存,这将不必要地抛出内存不足错误.
这种实现可以解决问题:
IntVec& operator=(IntVec const& that) {
if(that.size <= size) {
size = …Run Code Online (Sandbox Code Playgroud) 在阅读了C++中的复制构造函数和复制赋值运算符之后,我尝试创建一个简单的例子.虽然下面的代码片段显然有用,但我不确定我是否正确地实现了复制构造函数和复制赋值运算符.能不能指出是否有任何错误/改进或更好的例子来理解相关概念.
class Foobase
{
int bInt;
public:
Foobase() {}
Foobase(int b) { bInt = b;}
int GetValue() { return bInt;}
int SetValue(const int& val) { bInt = val; }
};
class Foobar
{
int var;
Foobase *base;
public:
Foobar(){}
Foobar(int v)
{
var = v;
base = new Foobase(v * -1);
}
//Copy constructor
Foobar(const Foobar& foo)
{
var = foo.var;
base = new Foobase(foo.GetBaseValue());
}
//Copy assignemnt operator
Foobar& operator= (const Foobar& other)
{
if (this != &other) // …Run Code Online (Sandbox Code Playgroud) 自2011年以来,我们同时拥有复制和移动任务.但是,这个答案非常有说服力地说,对于资源管理类,只需要一个赋值运算符.对于std::vector,例如,这看起来像
vector& vector::operator=(vector other)
{
swap(other);
return*this;
}
Run Code Online (Sandbox Code Playgroud)
这里重要的一点是,论证是以价值为基础的.这意味着在输入函数体的时刻,大部分工作已经通过构造来完成other(如果可能的话,通过移动构造函数,否则通过复制构造函数).因此,这会自动正确地实现复制和移动分配.
如果这是正确的,为什么(根据这个文档至少)std::vector 没有以这种方式实现?
编辑以解释这是如何工作的.请考虑other以下示例中的上述代码中发生的情况
void foo(std::vector<bar> &&x)
{
auto y=x; // other is copy constructed
auto z=std::move(x); // other is move constructed, no copy is ever made.
// ...
}
Run Code Online (Sandbox Code Playgroud) 我提到这个问题: 什么是复制和交换习惯用法?
实际上,上述答案导致以下实施:
class MyClass
{
public:
friend void swap(MyClass & lhs, MyClass & rhs) noexcept;
MyClass() { /* to implement */ };
virtual ~MyClass() { /* to implement */ };
MyClass(const MyClass & rhs) { /* to implement */ }
MyClass(MyClass && rhs) : MyClass() { swap(*this, rhs); }
MyClass & operator=(MyClass rhs) { swap(*this, rhs); return *this; }
};
void swap( MyClass & lhs, MyClass & rhs )
{
using std::swap;
/* to implement */
//swap(rhs.x, lhs.x);
} …Run Code Online (Sandbox Code Playgroud) 移动赋值运算符通常应声明为noexcept(即将类型存储在STL容器中).但是复制和交换习惯用法允许在一段代码中定义复制和移动赋值运算符.在这种情况下如何处理noexcept说明符?复制结构可以抛出,但我怀疑它是否可以违反noexcept说明符.
// Is it correct considering that T copy constructor can throw?
T& operator=(T other) noexcept;
Run Code Online (Sandbox Code Playgroud) 定义赋值运算符时,它总是如下所示:
class X {...};
X& X::operator=(...whatever...);
Run Code Online (Sandbox Code Playgroud)
也就是说,它具有返回类型"对X的引用".这里,参数(...whatever...)可以是X&,const X&只X在使用复制和交换习语时,或任何其他类型.
似乎很奇怪,无论参数如何,每个人都建议返回非const引用X.这显然允许表达式(a = b).clear(),这应该是好的.
我有不同的意见,我希望禁止般的表情(x=y).clear,(x=y)=z甚至x=y=z在我的代码.我的想法是,这些表达式在一行代码上做的事情太复杂了.所以我决定让我的赋值运算符返回void:
void X::operator=(X) {...}
void X::operator=(int) {...}
Run Code Online (Sandbox Code Playgroud)
这有哪些负面影响?(除了看起来与平常不同)
我的班级X可以用于标准容器(例如std::vector<X>)吗?
我正在使用C++ 03(如果重要的话).