zoukankan      html  css  js  c++  java
  • C++学习笔记,初始化列表与构造函数

    一,初始化列表

    在开始执行组成构造函数体的复合语句之前,所有的直接基类,虚基类,及非静态数据成员的初始化均已结束。成员初始化列表是能指定这些对象的非默认初始化之处,对于不能默认初始化的基类或非静态数据成员,例如引用和const限定的类型的成员,必须指定成员初始化器,对没有成员初始化器的匿名联合体或变体成员不进行初始化

    二.委托构造函数

    若类自身的名字在初始化器列表中作为 类或标识符 出现,则该列表必须仅由这一个成员初始化器组成;这种构造函数被称为委托构造函数(delegating constructor),而构造函数列表的仅有成员所选择的构造函数是目标构造函数

    此情况下,首先由重载决议选择目标构造函数并予以执行,然后控制返回到委托构造函数并执行其函数体。委托构造函数不能递归。

    1 class Foo {
    2 public: 
    3   Foo(char x, int y) {}
    4   Foo(int y) : Foo('a', y) {} // Foo(int) 委托到 Foo(char,int)
    5 };

    初始化顺序:列表中的成员初始化器的顺序是不相关的:

    1)若构造函数时最终派生类,则按基类声明的深度优先、从左到右的遍历中的出现的顺序(从左到右指的是基说明符列表中程现的),初始化各个虚基类

    2)然后,以在此类的基类说明符列表中出现的从左到右顺序,初始化各个直接基类

    3)然后,以类定义中的声明顺序,初始化非静态成员

    4)最后,执行构造函数体。

    三.转换构造函数

    不以说明符explicit 声明且可以单个参数调用 (C++11 前)的构造函数被称为转换构造函数(converting constructor)

    与只在直接初始化(包括static_cast显式转换)中被考虑的explicit构造函数不同,转换构造函数还作为用户定义的转换序列中的一部分,在复制初始化中受到考虑

    通常说法是转换构造函数指定了一个从其实参类型(若存在)到其类类型的隐式转换。注意非 explicit 用户定义转换函数也指定一个隐式转换。

    隐式声明的及用户定义的非 explicit 复制构造函数与移动构造函数也是转换构造函数。

    四,复制构造函数

    类名(类名 &)

    凡在对象从同类型的另一对象(以直接初始化或复制初始化)初始化时,调用复制构造函数。

    1)初始化:T a=b或T a(b);b为类型T

    2)函数实参传递f(a),其中a类型为T而f(T t);

    3函数返回:在如T f()这样函数内部return a;a类型为T,它没有移动构造函数

    隐式声明的复制构造函数

    若不对类提供任何用户定义的复制构造函数,编译器始终会声明一个复制构造函数,作为其类非explicit的inline public 成员。

    当以下各项均为真时,这个隐式声明的复制构造函数拥有形式 T::T(const T&):

    • T 的每个直接与虚基类 B 均拥有复制构造函数,其形参为 const B& 或 const volatile B&;
    • T 的每个类类型或类类型数组的非静态数据成员 M 均拥有复制构造函数,其形参为 const M& 或 const volatile M&。

    否则,隐式声明的复制构造函数是 T::T(T&)。(注意因为这些规则,隐式声明的复制构造函数不能绑定到 volatile 左值实参)。

    类可以拥有多个复制构造函数,如 T::T(const T&) 和 T::T(T&)。

    当存在用户定义的复制构造函数时,用户仍可用关键词 default 强迫编译器生成隐式声明的复制构造函数。(C++11)

     1 struct A
     2 {
     3     int n;
     4     A(int n = 1) : n(n) { }
     5     A(const A& a) : n(a.n) { } // 用户定义的复制构造函数
     6 };
     7  
     8 struct B : A
     9 {
    10     // 隐式默认构造函数 B::B()
    11     // 隐式复制构造函数 B::B(const B&)
    12 };
    13  
    14 struct C : B
    15 {
    16      C() : B() { }
    17  private:
    18      C(const C&); // 不可复制,C++98 风格
    19 };
    20  
    21 int main()
    22 {
    23     A a1(7);
    24     A a2(a1); // 调用复制构造函数
    25     B b;
    26     B b2 = b;
    27     A a3 = b; // 转换到 A& 并调用复制构造函数
    28     volatile A va(10);
    29     // A a4 = va; // 编译错误
    30  
    31     C c;
    32     // C c2 = c; // 编译错误
    33 }

    五,移动构造函数

    类名(类名 &&)

    当(直接初始化或复制初始化)从同类型的右值初始化对象时,调用移动构造构造函数,包含

    1)初始化T a =std::move(b);或T a(std::move(b)),b类型为T

    2)函数实参传递f(std::move(a));其中a类型为T而f为Ret f(T f);

    3)函数返回:在T f()的函数中 return a;其中啊类型为T,

    隐式声明的移动构造函数

    若不对类类型(struct、class 或 union)提供任何用户定义的移动构造函数,且下列各项均为真:

    • 没有用户声明的复制构造函数;
    • 没有用户声明的复制赋值运算符;
    • 没有用户声明的移动复制运算符;
    • 没有用户声明的析构函数;

    则编译器将声明一个移动构造函数,作为其类的非 explicit的 inline public 成员,签名为 T::T(T&&)

    类可以拥有多个移动构造函数,例如 T::T(const T&&) 和 T::T(T&&)。当存在用户定义的移动构造函数时,用户仍可用关键词 default 强制编译器生成隐式声明的移动构造函数。

     1 #include <string>
     2 #include <iostream>
     3 #include <iomanip>
     4 #include <utility>
     5  
     6 struct A
     7 {
     8     std::string s;
     9     int k;
    10     A() : s("test"), k(-1) { }
    11     A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!
    "; }
    12     A(A&& o) noexcept :
    13            s(std::move(o.s)),       // 类类型成员的显式移动
    14            k(std::exchange(o.k, 0)) // 非类类型成员的显式移动
    15     { }
    16 };
    17  
    18 A f(A a)
    19 {
    20     return a;
    21 }
    22  
    23 struct B : A
    24 {
    25     std::string s2;
    26     int n;
    27     // 隐式移动构造函数 B::(B&&)
    28     // 调用 A 的移动构造函数
    29     // 调用 s2 的移动构造函数
    30     // 并进行 n 的逐位复制
    31 };
    32  
    33 struct C : B
    34 {
    35     ~C() { } // 析构函数阻止隐式移动构造函数 C::(C&&)
    36 };
    37  
    38 struct D : B
    39 {
    40     D() { }
    41     ~D() { }          // 析构函数阻止隐式移动构造函数 D::(D&&)
    42     D(D&&) = default; // 强制生成移动构造函数
    43 };
    44  
    45 int main()
    46 {
    47     std::cout << "Trying to move A
    ";
    48     A a1 = f(A()); // 按值返回时,从函数形参移动构造其目标
    49     std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '
    ';
    50     A a2 = std::move(a1); // 从亡值移动构造
    51     std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '
    ';
    52  
    53     std::cout << "Trying to move B
    ";
    54     B b1;
    55     std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "
    ";
    56     B b2 = std::move(b1); // 调用隐式移动构造函数
    57     std::cout << "After move, b1.s = " << std::quoted(b1.s) << "
    ";
    58  
    59     std::cout << "Trying to move C
    ";
    60     C c1;
    61     C c2 = std::move(c1); // 调用复制构造函数
    62  
    63     std::cout << "Trying to move D
    ";
    64     D d1;
    65     D d2 = std::move(d1);
    66 }

    六,析构函数

    是对象生存期终结时调用的特殊成员函数,目的时释放对象可能在其生成期间获得的资源

    ~类名();virtual ~类名()虚析构函数在基类中常为必要的

    生存期结束包含:1)程序终止,对于静态存储的对象;2)退出线程,对于具有线程局部存储的对象

    3)作用域结束,对于具有自动存储期的对象和生存期因绑定到引用而延长的临时量;

    4)delete 表达式,对于具有动态存储的对象5)完整表达式的结尾,对于无名临时量;

    6)栈回朔,对于具有自动存储期的对象,当未捕捉的异常脱离其块时;

    虚析构函数:通过指向基类指针删除对象引发未定义行为,除非基类的析构函数为虚函数

    1 class Base {
    2  public:
    3     virtual ~Base() {}
    4 };
    5 class Derived : public Base {};
    6 Base* b = new Derived;
    7 delete b; // 安全

    纯虚构函数:析构函数可以声明为纯虚,例如对于需要声明为抽象类,但没有其他可声明为纯虚的适合函数的基类。这种析构函数必须有定义,因为在销毁派生类时,所有基类析构函数都总是得到调用:

    1 class AbstractBase {
    2  public:
    3     virtual ~AbstractBase() = 0;
    4 };
    5 AbstractBase::~AbstractBase() {}
    6 class Derived : public AbstractBase {};
    7 // AbstractBase obj;   // 编译错误
    8 Derived obj;           // OK

    七,复制赋值运算符

    类名 & 类名 :: operator= ( 类名 )

    若不对类类型(struct、class 或 union)提供任何用户定义的复制赋值运算符,则编译器将始终声明一个,作为类的 inline public 成员。当以下各项均为真时,这个隐式声明的复制赋值运算符拥有形式 T& T::operator=(const T&):

    • T 的每个直接基类 B 均拥有复制赋值运算符,其形参是 B 或 const B& 或 const volatile B&;
    • T 的每个类类型或类数组类型的非静态数据成员 M 均拥有复制赋值运算符,其形参是 M 或 const M& 或 const volatile M&。

    否则隐式声明的复制赋值运算符被声明为 T& T::operator=(T&)。(注意因为这些规则,隐式声明的复制赋值运算符不能绑定到 volatile 左值实参。)

    八.移动赋值运算符

    类名 & 类名 :: operator= ( 类名 && )

    若不对类类型(struct、class 或 union)提供任何用户定义的移动赋值运算符,且下列各项均为真:

    • 没有用户声明的复制构造函数;
    • 没有用户声明的移动构造函数;
    • 没有用户声明的复制赋值运算符;
    • 没有用户声明的析构函数,

    则编译器将声明一个移动赋值运算符,作为其类的 inline public 成员,并拥有签名 T& T::operator=(T&&)

    类可以拥有多个移动赋值运算符,如 T& T::operator=(const T&&) 和 T& T::operator=(T&&)。当存在用户定义的移动赋值运算符时,用户仍可用关键词 default 强迫编译器生成隐式声明的移动赋值运算符。

  • 相关阅读:
    数独游戏
    数据类型格式化函数(PostgreSQL),ORACLE支持
    DB2中有关日期和时间的函数,及应用
    XPath详解,总结(转)
    使用 DB2 Connect By 的分级查询(转)
    了解 DB2 Version 9.5 中的全局变量(转)
    DB2中游标的使用(转)
    如何启用和禁用oracle&DB2数据库外键约束(转)
    如何在 DB2 Universal Database 中暂时禁用触发器(转)
    DB2的一些常用SQL写法(转)
  • 原文地址:https://www.cnblogs.com/qq964107326/p/15024684.html
Copyright © 2011-2022 走看看