c++ c++-faq copy-constructor assignment-operator rule-of-three
因此,在观看了关于右值引用的精彩演讲之后,我认为每个类都会受益于这样的"移动构造函数",template<class T> MyClass(T&& other) 编辑,当然还有"移动赋值运算符",template<class T> MyClass& operator=(T&& other)正如Philipp在他的回答中指出的,如果它已经动态分配成员,或通常存储指针.就像你应该有一个copy-ctor,赋值运算符和析构函数,如果之前提到的点适用.思考?
我已经阅读了很多关于C++ 三规则的内容.很多人都发誓.但是当规则被陈述时,它几乎总是包括像"通常","可能"或"可能"这样的词,表示存在例外.我没有看到很多关于这些例外情况的讨论 - 三法则不成立的情况,或者至少在坚持它的情况下没有提供任何优势的情况.
我的问题是我的情况是否是三法则的合法例外.我相信在我下面描述的情况下,需要一个明确定义的复制构造函数和复制赋值运算符,但默认(隐式生成的)析构函数将正常工作.这是我的情况:
我有两个类,A和B.这里讨论的是A. B是A的朋友.A包含B对象.B包含一个A指针,用于指向拥有B对象的A对象.B使用此指针来操纵A对象的私有成员.除了A构造函数之外,B永远不会被实例化.像这样:
// A.h
#include "B.h"
class A
{
private:
B b;
int x;
public:
friend class B;
A( int i = 0 )
: b( this ) {
x = i;
};
};
Run Code Online (Sandbox Code Playgroud)
和...
// B.h
#ifndef B_H // preprocessor escape to avoid infinite #include loop
#define B_H
class A; // forward declaration
class B
{
private:
A * ap;
int y;
public:
B( A * a_ptr = 0 ) {
ap …Run Code Online (Sandbox Code Playgroud) 考虑以下程序:
#include <string>
struct S {
S (){}
private:
void *ptr = nullptr;
std::string str = "";
};
int main(){}
Run Code Online (Sandbox Code Playgroud)
在使用-Weffc++GCC 4.7.1 编译时,这将吐出:
warning: 'struct S' has pointer data members [-Weffc++] warning: but does not override 'S(const S&)' [-Weffc++] warning: or 'operator=(const S&)' [-Weffc++]
这通常没问题,除了这个例子的几个东西:
如果我注释掉任何构造函数,指针声明或字符串声明,警告就会消失.这很奇怪,因为你认为指针就足够了,但事实并非如此.此外,将字符串声明更改为整数声明也会导致它消失,因此只有在有字符串(或可能是其他选择类)时才会出现.为什么在这种情况下警告会消失?
通常,当所有指针都在指向现有变量(通常由OS维护)时,会出现此警告.没有new,没有delete.当复制带有句柄的类时,我不想要深层复制.我希望两个句柄指向同一个内部对象(例如窗口).有没有办法让编译器实现这一点,而不会不必要地重载复制构造函数和赋值运算符,或完全禁用警告#pragma?当三法则甚至不适用时,为什么我首先被困扰?
在定义接口类时声明实例化方法的正确方法是什么?
出于显而易见的原因,抽象基类需要具有虚拟析构函数.但是,然后给出以下编译警告:"'InterfaceClass'定义了一个非默认的析构函数,但没有定义复制构造函数,复制赋值运算符,移动构造函数或移动赋值运算符",这是"五的规则" ".
我理解为什么一般应该遵守"五规则",但是它仍适用于抽象基类或接口吗?
那么我的意思是:
class InterfaceClass
{
// == INSTANTIATION ==
protected:
// -- Constructors --
InterfaceClass() = default;
InterfaceClass(const InterfaceClass&) = default;
InterfaceClass(InterfaceClass&&) = default;
public:
// -- Destructors --
virtual ~InterfaceClass() = 0;
// == OPERATORS ==
protected:
// -- Assignment --
InterfaceClass& operator=(const InterfaceClass&) = default;
InterfaceClass& operator=(InterfaceClass&&) = default;
// == METHODS ==
public:
// Some pure interface methods here...
};
// == INSTANTIATION ==
// -- Destructors --
InterfaceClass::~InterfaceClass()
{
}
Run Code Online (Sandbox Code Playgroud)
它是否正确?这些方法应该= delete …
我正在学习c ++,最近我学习了(这里是堆栈溢出)有关复制和交换的习惯用法,我对它有一些问题.所以,假设我有以下类使用复制和交换习惯用法,例如:
class Foo {
private:
int * foo;
int size;
public:
Foo(size_t size) : size(size) { foo = new int[size](); }
~Foo(){delete foo;}
Foo(Foo const& other){
size = other.size;
foo = new int[size];
copy(other.foo, other.foo + size, foo);
}
void swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
}
Foo& operator=(Foo g) {
g.swap(*this);
return *this;
}
int& operator[] (const int idx) {return foo[idx];}
};
Run Code Online (Sandbox Code Playgroud)
我的问题是,假设我有另一个类,它有一个Foo对象作为数据但没有指针或其他可能需要自定义复制或赋值的资源:
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar …Run Code Online (Sandbox Code Playgroud) 我正在努力学习C++中的"三巨头"..我设法为"三巨头"做了非常简单的程序..但我不知道如何使用对象指针..以下是我的第一次尝试.
当我写这篇文章时,我有一个疑问......
问题
我是否正确地说我需要删除析构函数中的指针?
class TreeNode
{
public:
TreeNode();
TreeNode(const TreeNode& node);
TreeNode& operator= (const TreeNode& node);
~TreeNode();
private:
string data;
TreeNode* left;
TreeNode* right;
friend class MyAnotherClass;
};
Run Code Online (Sandbox Code Playgroud)履行
TreeNode::TreeNode(){
data = "";
}
TreeNode::TreeNode(const TreeNode& node){
data = node.data;
left = new TreeNode();
right = new TreeNode();
left = node.left;
right = node.right;
}
TreeNode& TreeNode::operator= (const TreeNode& node){
data = node.data;
left = node.left;
right = node.right;
return *this;
}
TreeNode::~TreeNode(){
delete …Run Code Online (Sandbox Code Playgroud) 什么是复杂对象的"最小框架"(必要的方法)(具有明确的malloced内部数据),我想要存储在STL容器中,例如<vector>?
对于我的假设(复杂对象Doit的例子):
#include <vector>
#include <cstring>
using namespace std;
class Doit {
private:
char *a;
public:
Doit(){a=(char*)malloc(10);}
~Doit(){free(a);}
};
int main(){
vector<Doit> v(10);
}
Run Code Online (Sandbox Code Playgroud)
给
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0804b008 ***
Aborted
Run Code Online (Sandbox Code Playgroud)
在valgrind:
malloc/free: 2 allocs, 12 frees, 50 bytes allocated.
Run Code Online (Sandbox Code Playgroud)
更新:
这种对象的最小方法是:(基于sbi答案)
class DoIt{
private:
char *a;
public:
DoIt() { a=new char[10]; }
~DoIt() { delete[] a; }
DoIt(const DoIt& rhs) { a=new char[10]; std::copy(rhs.a,rhs.a+10,a); }
DoIt& operator=(const DoIt& rhs) { …Run Code Online (Sandbox Code Playgroud) 12.8/7节中的标准说:
如果类定义没有显式声明复制构造函数,则会隐式声明一个.如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制构造函数被定义为已删除; 否则,它被定义为默认值(8.4).如果 类具有用户声明的复制赋值运算符或用户声明的 析构函数,则不推荐使用后一种情况.因此,对于类的定义
Run Code Online (Sandbox Code Playgroud)struct X { X(const X&, int); };隐式声明了复制构造函数.如果用户声明的构造函数稍后定义为
Run Code Online (Sandbox Code Playgroud)X::X(const X& x, int i =0) { /? ... ?/ }
我无法理解如果该类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况.在示例中,标准既不提供用户声明的复制赋值运算符也不提供析构函数.如果我们声明析构函数或复制赋值运算符会发生什么?我试过这样做如下:
struct A
{
~A(){ };
};
A::A(const A&){ }; //error
int main(){ }
Run Code Online (Sandbox Code Playgroud)
但在示例中,我们仍然具有隐式声明的复制构造函数.这个规则实际意味着什么?
我想如果我们写下面的内容:
struct A
{
A(){ };
A(const A&&){ };
~A(){ };
};
A a;
A t = a; //error: call to implicitly-deleted copy constructor of 'A'
int main()
{
}
Run Code Online (Sandbox Code Playgroud)
复制构造函数不会被显式删除.但事实并非如此.
根据下面广为人知的表格,当用户提供一个或多个复制赋值、复制构造函数和析构函数时,C++11 中不推荐自动编译器生成默认复制构造函数和复制赋值(红色单元格表示弃用)。考虑到“三法则”,这是完全有道理的。但是,该表显示,在用户提供的复制构造函数/赋值的情况下,默认析构函数的生成并未被弃用。
这个设计决定背后的理由是什么?

c++ ×10
rule-of-three ×10
c++11 ×2
constructor ×2
c++-faq ×1
c++17 ×1
class ×1
g++ ×1
pointers ×1
stl ×1