目录
一、初始化列表
1.1 - 定义
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
class Date
{
public:
    // 一、在构造函数体内进行赋值操作
    /*Date(int year = 1949, int month = 10, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }*/
    // 二、使用初始化列表
    Date(int year = 1949, int month = 10, int day = 1)
        : _year(year), _month(month), _day(day) {}
private:
    int _year;
    int _month;
    int _day;
};1.2 - 使用初始化列表的原因
-  性能原因。对于内置类型的成员变量,使用初始化列表和在构造函数体内赋值差别不是很大,但是对于类类型的成员变量来说,最好使用初始化列表。例如: #include <iostream> using namespace std;  class A { public: // 默认的构造函数 A(int x = 0) : _i(x) { cout << "A(int x = 0)" << endl; }  // 拷贝构造函数 A(const A& a) : _i(a._i) { cout << "A(const A& a)" << endl; }  // 赋值运算符重载 A& operator=(const A& a) { cout << "A& operator=(const A& a)" << endl; _i = a._i; return *this; }  void Print() const { cout << _i << endl; } private: int _i; };  class B { public: // 拷贝构造函数 B(const A& a) { _a.Print(); _a = a; _a.Print(); } private: A _a; };  int main() { A a(10); // A(int x = 0)  B b(a); // A(int x = 0) // 0 // A& operator=(const A& a) // 10 return 0; }class B { public: B(const A& a) : _a(a) { _a.Print(); } private: A _a; }; class B { public: B(int x = 0) : _a(x) { _a.Print(); } private: A _a; }; 
-  除了性能原因之外,有时候初始化列表是不可或缺的。类包含以下成员变量时,必须放在初始化列表位置进行初始化: - const成员变量,因为- const对象必须初始化。
- 引用类型的成员变量,因为引用必须初始化。
- 没有默认构造函数的类类型成员变量,因为使用初始化列表时不必调用默认构造函数来初始化,可以直接调用对应的构造函数来初始化。
 #include <iostream> using namespace std; class A { public: // 带参的构造函数 A(int x) : _i(x) { cout << "A(int x)" << endl; } private: int _i; }; class B { public: B(int y, int& r, int x) : _j(y), _r(r), _a(x) {} private: const int _j; // const 成员变量 int& _r; // 引用类型的成员变量 A _a; // 没有默认构造函数的类类型的成员变量 }; int main() { int n = 20; B b(10, n, 30); // A(int x) return 0; }
1.3 - 成员变量的初始化顺序
成员变量在类中声明的次序就是其在初始化列表中初始化的顺序,与其在初始化列表中的先后次序无关。
class A
{
public:
    A(int x = 0) : _j(x), _i(_j) {}
    void Print() const
    {
        cout << _i << " " << _j << endl;
    }
private:
    int _i;
    int _j;
};
int main()
{
    A a;
    a.Print();  // 随机值 0
    return 0;
}二、静态成员
2.1 - 静态成员变量
对象的内存中包含了成员变量,不同的对象占用不同的内存,这使得不同对象的成员变量相互独立,它们的值不受其他对象的影响。可是有时候我们希望在多个对象之间共享数据,在对象 a 改变了某份数据后,对象 b 可以检测到。共享数据的典型使用场景是计数。
在 C++ 中,可以使用静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量,它被关键字 static 修饰。
#include <iostream>
using namespace std;
class A
{
public:
    A() { ++_count; }
    A(const A& a) { ++_count; } 
    ~A() { --_count; }
    static int _count;  // 静态成员变量
};
int A::_count = 0;
int main()
{
    cout << A::_count << endl;  // 0
    A a1;
    A a2;
    A a3(a1);
    A a4(a2);
    cout << A::_count << endl;  // 4
    return 0;
}这段代码定义了一个类 A,其静态成员变量 _count 用来统计当前创建出来的类对象的个数。
static 成员变量的特性:
-  static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,也只为 _count分配一份内存,所有对象使用的都是这份内存中的数据。
-  static 成员变量不占用对象内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问,所以 sizeof(A) == 1。
-  static 成员变量既可以通过对象来访问,也可以通过类来访问,例如: A::_count。
-  static 成员变量必须在类的外部初始化,具体形式为: type class::name = value;
-  static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在类外初始化时分配,所以没有在类外部初始化的 static 成员变量不能使用。 
2.2 - 静态成员函数
在类中,static 除了可以声明静态成员变量,还可以声明静态成员函数。
编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把调用该函数的对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。
普通成员函数可以访问所有成员(包括成员变量和成员函数),而静态成员函数因为没有 this 指针,不知道指向哪个对象,所以无法访问对象的成员变量,即不能访问普通成员变量,也无法调用普通成员函数,只能访问静态成员变量和调用静态成员函数。
#include <iostream>
using namespace std;
class A
{
public:
    A() { ++_count; }
    A(const A& a) { ++_count; }
    ~A() { --_count; }
    static int GetCount() { return _count; }  // 静态成员函数
private:
    static int _count;   // 静态成员变量
};
int A::_count = 0;
int main()
{
    cout << A::GetCount() << endl;  // 0
    A a1;
    A a2;
    A a3(a1);
    A a4(a2);
    cout << A::GetCount() << endl;  // 4
    return 0;
}三、友元
私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这些固然能带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。
C++ 设计者认为,如果有的程序员真的非常怕麻烦,就是想在类的成员函数外部直接访问对象的私有成员,那还是做一点妥协以满足他们的愿望为好,这也算是眼前利益和长远利益的折中。因此,C++ 就有了友元(friend) 的概念。打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开自己的隐私。
友元分为两种:友元函数和友元类。
3.1 - 友元函数
在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为友元,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。注意:友元函数可以在类定义的任何地方声明,不受类访问限定符的限制。
将全局函数声明为友元的写法如下:
friend 返回值类型 函数名(参数列表);将其他类的成员函数声明为友元的写法如下:
friend 返回值类型 其他类的类名::成员函数名(参数列表);但是,不能把其他类的私有成员函数声明为友元。
示例:
#include <iostream>
using namespace std;
class Date
{
    friend ostream& operator<<(ostream& out, const Date& d);
    friend istream& operator>>(istream& in, Date& d);
public:
    Date(int year = 1949, int month = 10, int day = 1)
        : _year(year), _month(month), _day(day) {}
private:
    int _year;
    int _month;
    int _day;
};
// 流插入运算符(<<)重载
ostream& operator<<(ostream& out, const Date& d)
{
    out << d._year << "-" << d._month << "-" << d._day;
    return out;
}
// 流提取运算符(>>)重载
istream& operator>>(istream& in, Date& d)
{
    in >> d._year >> d._month >> d._day;
    return in;
}
int main()
{
    Date d;
    cout << d << endl;  // 1949-10-1
    cin >> d;  // 假设输入:2023 5 1
    cout << d << endl;  // 2023-5-1
    return 0;
}3.2 - 友元类
一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员。在类定义中声明友元类的写法如下:
friend class 类名;示例:
class Time
{
    friend class Date;
public:
    Time(int hour = 0, int minute = 0, int second = 0)
        : _hour(hour), _minute(minute), _second(second) {}
private:
    int _hour;
    int _minute;
    int _second;
};
class Date
{
public:
    Date(int year = 1949, int month = 10, int day = 1)
        : _year(year), _month(month), _day(day) {}
    void SetTimeOfDate(int hour, int minute, int second)
    {
        _t._hour = hour;
        _t._minute = minute;
        _t._second = second;
    }
private:
    int _year;
    int _month;
    int _day;
    Time _t;
};
int main()
{
    Date d(2023, 5, 1);
    d.SetTimeOfDate(12, 30, 00);
    return 0;
}
四、内部类
如果一个类定义在另一类的内部,这个内部类就叫作内部类。内部类是一个独立的类,它不属于外部内,更不能通过外部的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类是外部类的友元类,参考友元类的定义,内部类可以通过外部类的对象来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
-  内部类可以定义在外部类的 public、protected、private 都是可以的。 
-  内部类可以直接访问外部类的 static 成员,不需要外部类的对象/类名。 
-  sizeof(外部类) = 外部类,和内部类没有任何关系。
#include <iostream>
using namespace std;
class A
{
public:
    A(int x = 0) : _i(x) {}
    // 内部类
    class B
    {
    public:
        B(int y = 0) : _j(y) {}
        void func(const A& a)
        {
            cout << _s << " " << a._i << endl;
        }
    private:
        int _j;
    };
private:
    static int _s;
    int _i;
};
int A::_s = 1;
int main()
{
    cout << sizeof(A) << endl;  // 4
    A a(10);
    A::B b;
    b.func(a);  // 1 10
    return 0;
}五、匿名对象
#include <iostream>
using namespace std;
class A
{
public:
    A(int x = 0) : _i(x) 
    { 
        cout << "A(int x = 0)" << endl; 
    }
    ~A()
    {
        cout << "~A()" << endl;
    }
private:
    int _i;
};
int main()
{
    // 非匿名对象:
    // A a1;
    // A a2(10);
    // 匿名对象(顾名思义,即没有名字的对象):
    A();
    A(10);  
    return 0;
}5.1 - 匿名对象的特性
-  匿名对象的生命周期只有它所在的那一行:  
-  匿名对象具有常性: // A& ra = A(10); // error(权限放大) const A& ra = A(10); // ok
-  使用常引用会延长匿名对象的生命周期:  
5.2 - 匿名对象的使用场景
-  当方法只调用一次的时候就可以使用匿名对象: #include <iostream> using namespace std;  class Solution { public: int SumSolution(int n) { int sum = 0; for (int i = 1; i <= n; ++i) { sum += i; } return sum; } };  int main() { cout << Solution().SumSolution(100) << endl; // 5050 return 0; }
-  当作参数进行传递: #include <iostream> using namespace std;  class Date { friend void Display(const Date& d); public: Date(int year = 1949, int month = 10, int day = 1) : _year(year), _month(month), _day(day) {} private: int _year; int _month; int _day; };  void Display(const Date& d) { cout << d._year << "-" << d._month << "-" << d._day << endl; }  int main() { Display(Date(2023, 5, 1)); // 2023-5-1 return 0; }
六、编译器所做的一些优化
#include <iostream>
using namespace std;
class A
{
public:
	A(int x = 0) : _i(x) 
	{
		cout << "A(int x = 0)" << endl;
	}
	A(const A& a) : _i(a._i)
	{
		cout << "A(const A& a)" << endl;
	}
	A& operator=(const A& a)
	{
		cout << "A& operator=(const A& a)" << endl;
		if (this != &a)
		{
			_i = a._i;
		}
		return *this;
	}
	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _i;
};
void func1(A a)
{
}
A func2()
{
	A a;
	return a;
}
int main()
{
	func1(10);  
	// 构造(隐式类型转换)+ 拷贝构造 --> 优化成一个构造
	cout << "------------------" << endl;
	func1(A(10));
	// 构造 + 拷贝构造 --> 优化成一个构造
	cout << "------------------" << endl;
	A ret1 = func2(); 
	// 在一个表达式中,连续的拷贝构造 --> 优化成一个拷贝构造
	cout << "------------------" << endl;
	// A ret2;
	// 1. 构造
	// ret2 = func2();
	// 2. 赋值运算符重载
	// 编译器无法优化
	return 0;
}









