考虑以下代码:
#include <utility>
namespace ns
{
struct foo
{
foo() : i(0) {}
int i;
private:
foo(const foo&); // not defined,
foo& operator=(const foo&); // non-copyable
};
void swap(foo& lhs, foo& rhs)
{
std::swap(lhs.i, rhs.i);
}
}
template <typename T>
void do_swap(T& lhs, T& rhs); // implementation to be determined
int main()
{
ns::foo a, b;
do_swap(a, b);
}
Run Code Online (Sandbox Code Playgroud)
在C++ 03中,这种实现do_swap将被视为"已损坏":
template <typename T>
void do_swap(T& lhs, T& rhs)
{
std::swap(lhs, rhs);
}
Run Code Online (Sandbox Code Playgroud)
通过明确指定std::,它禁止ns::swap …
在我学习C++的过程中,我偶然发现了编写复制构造函数和赋值运算符的文章,该文章提出了一种机制来避免复制构造函数和赋值运算符之间的代码重复.
为了总结/复制该链接的内容,建议的机制是:
struct UtilityClass
{
...
UtilityClass(UtilityClass const &rhs)
: data_(new int(*rhs_.data_))
{
// nothing left to do here
}
UtilityClass &operator=(UtilityClass const &rhs)
{
//
// Leaves all the work to the copy constructor.
//
if(this != &rhs)
{
// deconstruct myself
this->UtilityClass::~UtilityClass();
// reconstruct myself by copying from the right hand side.
new(this) UtilityClass(rhs);
}
return *this;
}
...
};
Run Code Online (Sandbox Code Playgroud)
这似乎是避免代码重复同时确保"编程完整性"的好方法,但需要权衡浪费工作的风险 - 然后分配嵌套内存,而不是重新使用(正如其作者指出的那样).
但我不熟悉其核心语法:
this->UtilityClass::~UtilityClass()
Run Code Online (Sandbox Code Playgroud)
我假设这是一种调用对象的析构函数(破坏对象结构的内容)同时保持结构本身的方法.对于C++新手来说,语法看起来像是对象方法和类方法的奇怪混合.
有人可以向我解释这个语法,还是指向一个可以解释它的资源?
该呼叫与以下内容有何不同?
this->~UtilityClass()
Run Code Online (Sandbox Code Playgroud)
这是合法的电话吗?这是否会破坏对象结构(没有堆;从堆栈中弹出)?
我想创建一个名为pos(从位置)的typedef结构来存储坐标x和y.我试图为这个结构重载一些运算符,但它不编译.
typedef struct {
int x;
int y;
inline pos operator=(pos a) {
x=a.x;
y=a.y;
return a;
}
inline pos operator+(pos a) {
return {a.x+x,a.y+y};
}
inline bool operator==(pos a) {
if (a.x==x && a.y== y)
return true;
else
return false;
}
} pos;
Run Code Online (Sandbox Code Playgroud)
我也想知道这个之间的区别:
inline bool operator==(pos a) {
if(a.x==x && a.y== y)
return true;
else
return false;
}
Run Code Online (Sandbox Code Playgroud)
还有这个:
bool operator==(pos a) const {
if(a.x==x && a.y== y)
return true;
else
return false;
}
Run Code Online (Sandbox Code Playgroud) 在一些c ++实践中,我试图学习并采用复制交换习语,对这个问题进行彻底的解释:复制交换习语.
但我发现了一些我从未见过的代码:using std::swap; // allow ADL在这个例子中
class dumb_array
{
public:
// ...
void swap(dumb_array& pOther) // nothrow
{
using std::swap; // allow ADL /* <===== THE LINE I DONT UNDERSTAND */
swap(mSize, pOther.mSize); // with the internal members swapped,
swap(mArray, pOther.mArray); // *this and pOther are effectively swapped
}
};
Run Code Online (Sandbox Code Playgroud)
using std::swap;在函数实现的主体内部意味着什么?测试新的Move Semantics.
我刚刚问了一下移动构造函数我遇到的问题.但是,正如评论中所述,问题实际上是"移动分配"操作符和"标准分配"操作符在使用标准的"复制和交换"惯用法时发生冲突.
这是我正在使用的课程:
#include <string.h>
#include <utility>
class String
{
int len;
char* data;
public:
// Default constructor
// In Terms of C-String constructor
String()
: String("")
{}
// Normal constructor that takes a C-String
String(char const* cString)
: len(strlen(cString))
, data(new char[len+1]()) // Allocate and zero memory
{
memcpy(data, cString, len);
}
// Standard Rule of three
String(String const& cpy)
: len(cpy.len)
, data(new char[len+1]())
{
memcpy(data, cpy.data, len);
}
String& operator=(String rhs)
{
rhs.swap(*this);
return *this;
} …Run Code Online (Sandbox Code Playgroud) 今天我了解到swap不允许在C++中抛出异常.
我也知道以下内容也不能抛出异常:
还有其他人吗?
或许,是否有某种列表提到可能不会抛出的一切?
(显然,比标准本身更简洁.)
我很困惑何时调用移动构造函数与复制构造函数.我已经阅读了以下资料:
所有这些来源要么过于复杂(我只想要一个简单的例子),要么只展示如何编写移动构造函数,而不是如何调用它.我写了一个简单的问题更具体:
const class noConstruct{}NoConstruct;
class a
{
private:
int *Array;
public:
a();
a(noConstruct);
a(const a&);
a& operator=(const a&);
a(a&&);
a& operator=(a&&);
~a();
};
a::a()
{
Array=new int[5]{1,2,3,4,5};
}
a::a(noConstruct Parameter)
{
Array=nullptr;
}
a::a(const a& Old): Array(Old.Array)
{
}
a& a::operator=(const a&Old)
{
delete[] Array;
Array=new int[5];
for (int i=0;i!=5;i++)
{
Array[i]=Old.Array[i];
}
return *this;
}
a::a(a&&Old)
{
Array=Old.Array;
Old.Array=nullptr;
}
a& a::operator=(a&&Old)
{
Array=Old.Array;
Old.Array=nullptr;
return *this;
}
a::~a()
{
delete[] Array;
} …Run Code Online (Sandbox Code Playgroud) 如本回答所述,复制和交换习惯用法如下实现:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
Run Code Online (Sandbox Code Playgroud)
通过将MyClass的值作为operator =的参数,可以通过复制构造函数或移动构造函数构造参数.然后,您可以安全地从参数中提取数据.这可以防止代码重复并有助于异常安全.
答案提到您可以在临时中交换或移动变量.它主要讨论交换.但是,交换(如果未由编译器优化)涉及三个移动操作,而在更复杂的情况下,还需要额外的额外工作.当你想要的时候,就是将临时文件移动到assign-to对象中.
考虑这个更复杂的例子,涉及观察者模式.在这个例子中,我手动编写了赋值运算符代码.重点是移动构造函数,赋值运算符和交换方法:
class MyClass : Observable::IObserver …Run Code Online (Sandbox Code Playgroud) C++ 11"移动"是一个很好的功能,但我发现当与"复制"同时使用时,很难避免代码重复(我们都讨厌这个).下面的代码是我实现的一个简单的循环队列(不完整),两个push()方法几乎相同,除了一行.
我遇到过很多像这样的情况.任何想法如何避免这种代码重复而不使用宏?
===编辑===
在这个特定的例子中,重复的代码可以被重构并放入一个单独的函数中,但有时这种重构是不可用的或者不能轻易实现.
#include <cstdlib>
#include <utility>
template<typename T>
class CircularQueue {
public:
CircularQueue(long size = 32) : size{size} {
buffer = std::malloc(sizeof(T) * size);
}
~CircularQueue();
bool full() const {
return counter.in - counter.out >= size;
}
bool empty() const {
return counter.in == counter.out;
}
void push(T&& data) {
if (full()) {
throw Invalid{};
}
long offset = counter.in % size;
new (buffer + offset) T{std::forward<T>(data)};
++counter.in;
}
void push(const T& data) {
if (full()) { …Run Code Online (Sandbox Code Playgroud) 通过使用Copy&Swap习语,我们可以轻松实现具有强大异常安全性的副本分配:
T& operator = (T other){
using std::swap;
swap(*this, other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
但是这需要T是可交换的.如果std::is_move_constructible_v<T> && std::is_move_assignable_v<T> == true感谢,自动哪种类型std::swap.
我的问题是,使用"复制和移动"成语有没有任何缺点?像这样:
T& operator = (T other){
*this = std::move(other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
只要你实现了move-assignment,T因为很明显你最终会得到无限递归.
这个问题不同于复制和交换成语是否应该成为C++ 11中的复制和移动成语?因为这个问题更通用,并且使用移动赋值运算符而不是手动移动成员.这避免了在链接线程中预测答案的清理问题.