C++ Special members

Special members

Special member functions are member functions that are implicitly defined as member of classes under certain circumstances. There are six:

Special member functions are member functions that are implicitly defined as member of classes under certain circumstances. There are six:

Default constructor

The default constructor is the constructor called when objects of a class are declared, but are not initialized with any arguments.

If a class definition has no constructors, the compiler assumes the class to have an implicitly defined default constructor. Therefore, after declaring a class like this:

如果一个类并未定义构造器时,那么C++就会为其设计一个默认的隐式构造器。

class Example {
  public:
    int total;
    void accumulate (int x) { total += x; }
};

The compiler assumes that Example has a default constructor. Therefore, objects of this class can be constructed by simply declaring them without any arguments:

这个构造器认为这个实例有一个默认的构造器,因此,这个类的对象在被定义时可以不用任何参数。

Example ex;

But as soon as a class has some constructor taking any number of parameters explicitly declared, the compiler no longer provides an implicit default constructor, and no longer allows the declaration of new objects of that class without arguments. For example, the following class:

class Example2 {
  public:
    int total;
    Example2 (int initial_value) : total(initial_value) { };
    void accumulate (int x) { total += x; };
};

但一旦这个类有一些传递参数,那么编译器就不会再提供一个默认的构造器。并不会在允许一个没有参数的对象来声明了,上面那个就是这个例子,你无法直接没有参数的声明。

Example2 ex (100); // ok: calls constructor

Example2 ex; // not valid: no default constructor

Therefore, if objects of this class need to be constructed without arguments, the proper default constructor shall also be declared in the class. For example:

所以,如果一个类的对象需要被构造器没有参数的声明,那么正确的默认构造器将被在一个类中声明,例如:

// default and delete implicit members
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle (int x, int y) : width(x), height(y) {}
    Rectangle() = default;
    Rectangle (const Rectangle& other) = delete;
    int area() {return width*height;}
};

int main () {
  Rectangle foo;
  Rectangle bar (10,20);

  cout << "bar's area: " << bar.area() << '\n';
  return 0;
}

这里利用 函数重载 的有关性质,根据传入参数的不同来调用名字相同但内部不同的函数,这里default是一个关键字,需要你明确的。

Destructor 析构函数

Destructors fulfill the opposite functionality of constructors: They are responsible for the necessary cleanup needed by a class when its lifetime ends. The classes we have defined in previous chapters did not allocate any resource and thus did not really require any clean up.

析构函数,负责当某些类寿命结束时,进行有关清理操作,我们往下看就很明白。

But now, let’s imagine that the class in the last example allocates dynamic memory to store the string it had as data member; in this case, it would be very useful to have a function called automatically at the end of the object’s life in charge of releasing this memory. To do this, we use a destructor. A destructor is a member function very similar to a default constructor: it takes no arguments and returns nothing, not even void. It also uses the class name as its own name, but preceded with a tilde sign (~):

当我们用一个对象申请一个动态内存时,当这个内存使用完了,我们不再需要这个内存了,这时,我们就需要析构函数来帮助我们释放内存的操作,注意,这个过程是系统自发完成的,不需要我们进行任何干涉,根据后面的操作来自行判断这个对象的声明周期是否结束。

// destructors
#include <iostream>
#include <string>
using namespace std;

class Example4 {
    string* ptr;
  public:
    // constructors:
    Example4() : ptr(new string) {}
    Example4 (const string& str) : ptr(new string(str)) {}
    // destructor:
    ~Example4 () {delete ptr;}
    // access content:
    const string& content() const {return *ptr;}
};

int main () {
  Example4 foo;
  Example4 bar ("Example");

  cout << "bar's content: " << bar.content() << '\n';
  return 0;
}

析构函数的标识是前面的~
注意,这个Example4() : ptr(new string) {}这个句子是默认不带参数的构造器,但这个构造器并不是什么不错,而是将指针 ptr指向一个新的字符串,这个字符串是空的,但不代表没有,可以等待以后赋值,就想下面这个样子。

*foo.ptr="hello world\n";

cout << "bar's content: " << foo.content()<< '\n';

但记住将要ptr设为静态的,如果没有那部初始化,那么将不会赋值,这个操作是你需要明确的。

Copy constructor

When an object is passed a named object of its own type as argument, its copy constructor is invoked in order to construct a copy.

即当一个对象被当作参数传递给一个相同的对象时,它的复制构造器被调用来构造一个副本。

A copy constructor is a constructor whose first parameter is of type reference to the class itself (possibly const qualified) and which can be invoked with a single argument of this type. For example, for a class MyClass, the copy constructor may have the following signature:

一个复制构造器是一个它的第一个参数类型是一个类本身的,可能被定义为常量类型,并且这可以被调用一个单独的这个类型的参数。例如,对于一个类,这个复制构造器可能有下列的特征:

MyClass::MyClass (const MyClass&);

If a class has no custom(定义)copy nor move constructors (or assignments) defined, an implicit copy constructor is provided. This copy constructor simply performs a copy of its own members. For example, for a class such as:

如果一个类没有自定义副本,也没有定义移动构造函数(或赋值),则提供隐式复制构造函数。此复制构造函数只执行其成员的副本。

class MyClass {
  public:
    int a, b; string c;
};

An implicit copy constructor is automatically defined. The definition assumed for this function performs a shallow copy, roughly equivalent to:

MyClass::MyClass(const MyClass& x) : a(x.a), b(x.b), c(x.c) {}

一个隐式的副本构造器自动地被定义,这是一种浅拷贝,这个定义默认如上面所示。

This default copy constructor may suit the needs of many classes. But shallow copies only copy the members of the class themselves, and this is probably not what we expect for classes like class Example4 we defined above, because it contains pointers of which it handles its storage. For that class, performing a shallow copy means that the pointer value is copied, but not the content itself; This means that both objects (the copy and the original) would be sharing a single string object (they would both be pointing to the same object), and at some point (on destruction) both objects would try to delete the same block of memory, probably causing the program to crash on runtime. This can be solved by defining the following custom copy constructor that performs a deep copy:

浅拷贝可以拷贝其类的成员,这个可能满足大多数的类的需求,但这里有一个问题,就是对于指针,那么它同样会拷贝指针,而现在问题是如果我们在副本中通过指针删除一个值,那么这个操作是针对内存进行操作的,同样会删除原来的对象中的值,对于这个缺陷,我们可以通过深拷贝来解决。

// copy constructor: deep copy
#include <iostream>
#include <string>
using namespace std;

class Example5 {
    string* ptr;
  public:
    Example5 (const string& str) : ptr(new string(str)) {}
    ~Example5 () {delete ptr;}
    // copy constructor:
    Example5 (const Example5& x) : ptr(new string(x.content())) {}
    // access content:
    const string& content() const {return *ptr;}
};

int main () {
  Example5 foo ("Example");
  Example5 bar = foo;

  cout << "bar's content: " << bar.content() << '\n';
  return 0;
}

这个析构器会自动释放内存。~Example5 () {delete ptr;},如果没有构造器,那么会显示内存错误。我们可以删除析构器和复制构造器来达到目的,但是,我们这里用这种方法来将原对象的字符串提出来来重新给新的对象的字符串复制,这种操作是你要理解的,除此之外就很简单了。

Copy assignment 复制赋值

Objects are not only copied on construction, when they are initialized: They can also be copied on any assignment operation. See the difference:

对象不仅仅可以被构造器所复制,当它们在初始化时,它们也可以被其他任何赋值操作所复制。

MyClass foo;
MyClass bar (foo);       // object initialization: copy constructor called
MyClass baz = foo;       // object initialization: copy constructor called
foo = bar;

Note that baz is initialized on construction using an equal sign, but this is not an assignment operation! (although it may look like one): The declaration of an object is not an assignment operation, it is just another of the syntaxes to call single-argument constructors.

注意,这里等号,并不是一个赋值操作符,虽然它看起来很像。它仅仅是另一种调用单独参数构造的语法。

The assignment on foo is an assignment operation. No object is being declared here, but an operation is being performed on an existing object; foo.

The copy assignment operator is an overload of operator= which takes a value or reference of the class itself as parameter. The return value is generally a reference to *this (although this is not required). For example, for a class MyClass, the copy assignment may have the following signature:

这里就是对这个赋值操作符重载,来达到复制的操作,将等号后面的这个对象作为参数来传递给前面的对象的构造器,就是一种特殊的函数。

MyClass& operator= (const MyClass&);

he copy assignment operator is also a special function and is also defined implicitly if a class has no custom copy nor move assignments (nor move constructor) defined.

这个也作为一个隐式函数,如果没单独声明,与上面的相同,它也会进行浅复制。

当然可以构造函数来达到深复制的目的。

Example5& operator= (const Example5& x) {
  delete ptr;                      // delete currently pointed string
  ptr = new string (x.content());  // allocate space for new string, and copy
  return *this;
}

或者这样写更好:

Example5& operator= (const Example5& x) {
  *ptr = x.content();
  return *this;
}

Move constructor and assignment

Similar to copying, moving also uses the value of an object to set the value to another object. But, unlike copying, the content is actually transferred from one object (the source) to the other (the destination): the source loses that content, which is taken over by the destination. This moving only happens when the source of the value is an unnamed object.

与简单地拷贝相同,移动也适用一个对象的值来设置另一个对象的值,但是,不想拷贝,这内容真实的从一个来传递到另一个,也就是说,初始地址的值会丢失,这种操作是需要明确的。

这移动仅仅会发生在当源地址的值是一个未命名的对象时?

Unnamed objects are objects that are temporary in nature, and thus haven’t even been given a name. Typical examples of unnamed objects are return values of functions or type-casts.

未命名对象是一种临时性质的,因此并没有给予一个名字。典型的未命名对象的是一种函数值的返回或者类型转换。

Using the value of a temporary object such as these to initialize another object or to assign its value, does not really require a copy: the object is never going to be used for anything else, and thus, its value can be moved into the destination object. These cases trigger the move constructor and move assignments:

The move constructor is called when an object is initialized on construction using an unnamed temporary. Likewise, the move assignment is called when an object is assigned the value of an unnamed temporary:

MyClass fn();            // function returning a MyClass object
MyClass foo;             // default constructor
MyClass bar = foo;       // copy constructor
MyClass baz = fn();      // move constructor
foo = bar;               // copy assignment
baz = MyClass();         // move assignmen

注意,使用这个()来声明有关函数时,是返回的一个对象类型,并不是来运行默认的构造器。

Both the value returned by fn and the value constructed with MyClass are unnamed temporaries. In these cases, there is no need to make a copy, because the unnamed object is very short-lived and can be acquired by the other object when this is a more efficient operation.

这种声明方法声明出来的 unnamed object 是生存时间很短的。

The move constructor and move assignment are members that take a parameter of type rvalue reference to the class itself:

MyClass (MyClass&&);             // move-constructor
MyClass& operator= (MyClass&&);  // move-assignment 

移动构造器和移动赋值的有关操作。

An rvalue reference is specified by following the type with two ampersands (&&). As a parameter, an rvalue reference matches arguments of temporaries of this type.

这个操作符对应的是“暂存参量”。

move和copy概念的区别:
The concept of moving is most useful for objects that manage the storage they use, such as objects that allocate storage with new and delete. In such objects, copying and moving are really different operations:
- Copying from A to B means that new memory is allocated to B and then the entire content of A is copied to this new memory allocated for B.
- Moving from A to B means that the memory already allocated to A is transferred to B without allocating any new storage. It involves simply copying the pointer.

// move constructor/assignment
#include <iostream>
#include <string>
using namespace std;

class Example6 {
    string* ptr;
  public:
    Example6 (const string& str) : ptr(new string(str)) {}
    ~Example6 () {delete ptr;}
    // move constructor
    Example6 (Example6&& x) : ptr(x.ptr) {x.ptr=nullptr;}
    // move assignment
    Example6& operator= (Example6&& x) {
      delete ptr; 
      ptr = x.ptr;
      x.ptr=nullptr;
      return *this;
    }
    // access content:
    const string& content() const {return *ptr;}
    // addition:
    Example6 operator+(const Example6& rhs) {
      return Example6(content()+rhs.content());
    }
};


int main () {
  Example6 foo ("Exam");
  Example6 bar = Example6("ple");   // move-construction

  foo = foo + bar;                  // move-assignment

  cout << "foo's content: " << foo.content() << '\n';
  re
  ```

  这里的`=`被设计成成为一种move函数

  Compilers already optimize many cases that formally require a move-construction call in what is known as Return Value Optimization. Most notably, when the value returned by a function is used to initialize an object. In these cases, the move constructor may actually never get called.

  **操作符重载**
  这里表示一个一元操作符重载的例子。

  ```
  #include <iostream>

using namespace std;

class Student
{
  int num;

  public:
    Student (int a) :num(a){};
    void set(int x){this->num=x;};
    int content(){return num;};
    //复制赋值操作

    Student operator-()
    {
      num=-num;

      return Student(num);
    }
};

int main()
{
  int x;

  cin >> x;

  Student t(x);

  -t;

  cout << t.content() << endl;

  return 0;
}

Implicit members

The six special members functions described above are members implicitly declared on classes under certain circumstances:

隐式成员

当我们需要对这些删除或者重新编写或者调用时,可以直接这样

function_declaration = default;
function_declaration = delete;
// default and delete implicit members
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle (int x, int y) : width(x), height(y) {}
    Rectangle() = default;
    Rectangle (const Rectangle& other) = delete;
    int area() {return width*height;}
};

int main () {
  Rectangle foo;
  Rectangle bar (10,20);

  cout << "bar's area: " << bar.area() << '\n';
  return 0;
}

上面这代码可以采用默认定义的方法,即不穿入参数就能完成变量的定义。
并且删除了复制构造的方法,这些东西是需要你看懂的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值