[Elementary C++] 6. Classes and objects (initialization list, static members, friends, internal classes)

=========================================================================

Aikanyo gitee own take

C language learning diary: keep working hard (gitee.com)

 =========================================================================

approach period:

[Elementary C++] 5. Classes and Objects
(Improvement of the date class, stream operator overloaded functions, const members, "&" address operator overloading) - CSDN Blog

 =========================================================================

                     

Table of contents

        1. Initialization list

Assignment within constructor body

The role and use of initialization list

The role of the initialization list:

Usage of initialization list:​​​​​​​

Supplement: explicit keyword


2. static members

static member concept:

static member characteristics:


3. Friendship

friend function

Friend class


4. Internal classes

The concept of inner class:

Characteristics of inner classes:


Supplement: Some compiler optimizations when copying objects 

Supplement: Call the copy constructor or the "=" assignment operator overloaded function

Optimization 1: "Uninitialized object = built-in type"

Optimization 2: "Call function through anonymous object"

Optimization three: "Call functions through built-in types"

Optimization 4: "Uninitialized objects receive temporary objects returned by function values"


Code related to this blog:

Test.cpp statement -- C++ statement:​​​​​​​​

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

1. Initialization list

Assignment within constructor body

              

When creates the object, the compiler a>Call the constructor by, and give each member variable in the object a suitable a> already has an initial value in the object,< /span>can be assigned multiple times is thatThe body of the constructor and , initialization can only be initialized once Because . cannot be called initializationand ,assigning an initial value can only be called The statement in the constructor body objectBut it cannot yet be called an initialization of member variables in the , although, After executing the assignment in the constructor body, After calling the constructor, Initial value



                     

                     


                    

The role and use of initialization list

                 

The role of the initialization list:

                

  • The role of the initialization list can be simply understood as: inInitialize member variables first when defining them
    (Execute firstInitialization list,Execute againContents in the constructor body)
                        
  • When a class's member variable contains:
    reference variable type, const member variable, custom type member (and When there is no default constructor in this class)
    When there are these three categories When member variables, must use the initialization list
                     
  • For of in the member variable: reference variable type and const member variables, Both member variables are; cannot meet this condition assignment within the constructor body, requires that it be initialized when defined

                     
  • And for of in the member variable: custom Type member (andthere is no default constructor in the class), When this kind of member variable is initializedthe compilerusuallycalls its default constructor, but because has no default constructor, only Constructor with parameters,So it is necessary to call the default constructor before the compiler, a>Just first call it through theinitialization list Constructor with parameters is initialized, This is also assignment in the constructor body< /span>Unachievable





                         

---------------------------------------------------------------------------------------------

                  

Use of initialization list:

                       

  • Used case form:
    more than onefirst numberbeginning a>、adhesion is one pieceafternumber minutes apart Target number of staff members list Table of expressionOrfirst readingin the bracketsback face one piece release

                     
Precautions:
  • Each member variable can only appear once in the initialization list ( can only be initialized once )
                      
  • The class contains the following members, must be placed at the initialization list position< a i=4>Initialize: Reference member variables, const member variables , Custom type members (and This class has no default constructor< /span>)
Illustration:

                   

                   

  • Try touseinitialization list for initialization, a>Because regardless of whether you use an initialization list,
    forcustom type member variables a> be initialized using the initialization list first will definitely,
                  
  • The declaration order of member variables in the class is Its initialization order in the initialization list, and its order in the initialization list< a i=8> has nothing to do with , so it is best to member variables< /span> a>in the initialization list is consistent with the order of The declaration order

Illustration:

                     

                     


                    

Supplement: explicit keyword

               

  • Constructor can not onlyconstruct and initialize objects< /span>, constructor 's except The first parameter has no default value and the rest have default values ​​ or single parameter
    for , implicit type conversion

                 
  • When using thisimplicit type conversion, Codereadabilitymaybenot very good,< a i=9>Some places maynot allow this implicit type conversion to occur, then You can use the explicit keyword to modify the constructor,disable implicit conversion of the constructorThis will

Illustration:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2. static members

static member concept:

                  

Declare asstatic class members are called classes Static members,

Member variables modified withstatic are calledstatic member variables; A member function modified with static is called a static member function .

MeanwhileQuiet formation of staffHere we arePlacement statement ,existbeginning of outside(实现 / set义

                       

                       

---------------------------------------------------------------------------------------------

                  

static member characteristics:

              

  • Static members are shared by all class objects, Does not belong to a specific object, is stored in the static area a>
                    
  • Static member variables must be initializedoutside the class ( Implementation / Definition),
    does not need to add the static keyword when defining a> is just a statement in the class,
                    
  • Class static members can be used Class name::static members orObject.Static members to access
                  
  • Static member functionThere is no hidden this pointer, SoCannot access any non-static member ,
    but non-static member is< /span> >)< /span>static member function can access its ,Class domain the static member function As long as there is a way to find ( class through the the classYou can find the static member functions in , corresponding classescan find theirnon-static membersBecause,'sstatic member functioncan access the class's


                 
  • Quiet memberAlsoSerious member
    That's also Received public , protected , private access restriction marklimitation restriction
Illustration:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

3. Friendship

                    ​​​​​​​

Friends provide a wayto break through encapsulation,Sometimes we canprovide convenience.
But friends will increase the coupling of the code, to a certain extent< /span>Friend classAndFriend functions: ​​​​​​​Friends are divided into . should not be used more often so friends , destroys the encapsulation

                  

friend function

           

First half of the year's exhibition<<sum >>two-flow operation mark Going onJob

implementation<<style Multiplication mark multiplication function sum >>Flow calculation multiplication function , actual process,

We found thatcannotimplement bothin the class, overloading is defined as member variable ,
because these two stream operators pair The left operand has certain requirements,
itsleft operation The number is generally cout output stream object or cin input stream object,< /span>, is overloaded as a member function
If itThe hidden this pointerwillpreempt the position of its left operand (The position of the first parameter)

                    

  • So thesetwo stream operators should be overloaded as global functions,friendsAt this time, you need to solve the problem through , being unable to access private member variables in the classglobal functions outside the class
    But this will result in

                    
  • Friend function can directly access the private members of the class, It is an ordinary functiondefined outside the class, a>needs to be declared inside the classfriend keyword  needs to be added when declaring , but,
    does not belong to any class
                     
  • friend functionpossible to use in questionprivate(privateprotectionprotectedSenior
    但其Unusual member function
                 
  • Friend functionfor impossibleconstadvancement practice
                
  • Friend functions can be declaredanywhere in the class definition, and its is not restricted by the class access qualifier
                 
  • A functioncan bea friend function of multiple classes
                 
  • Friend function call and ordinary function call have the same principle
Illustration:

                     

                     


                    

Friend class

                

Allmember functions of a friend class can beof another class Both friend functions and
can access .
              

  • Tomomotokan systemistowardto,< /span>exist,B classA classWith disability:LikeReplacement with disability中文B category为其Tomomoto类中么过程类B中< a i=23>Direct access to A categoryPrivate management staff,HoweverA categoryinsidePrivate membership in class B Misbehavior



                 
  • Tomomotokan systemUnable to send
    LikeC< /span>tomomotoArepresentativeCnot representative,friend FormertoAisB, tomomototoBis
               
  • Friend relationship cannot be inherited (Inheritance will I learned about it later)
Illustration:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

4. Internal classes

The concept of inner class:

             

If a class is defined inside another class,Thenthis class is calledinner class.
Inner class is an independent class, Itdoes not belong toexternal class, more You cannot access the members of the inner class through the object of the outer class. Outer class versus inner classNoanySuperioraccess rights

             

Notice:

Inner class is the friend class ofouter class,According to the definition of friend class,
inner class can pass outer class Object parameterstoaccess all members in the outer class.
Butthe outer class is not a friend of the inner class

                       

                       

---------------------------------------------------------------------------------------------

                  

Characteristics of inner classes:

               

  • generalinternal categorydefined hereexternal category public , protected, private medium It is possible to meet,
    internal categoryalso to receivemutual access limit. Restrictions
                  
  • Inner class can directly accessstatic members in external class,< a i=4>No need to pass external class’s object / class name
                   
  • calculationexternal sizesizeof(Japan's internal exclusion system ,External category size=)External category
Illustration:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

Supplement: Some compiler optimizations when copying objects 

The process of passing parameters and passing return values in In, generally the compiler will do someOptimize, Reduce the copy of objects, This is useful in some scenarios It is still very useful, Note: the following Optimization are all based onthe same expression

                   

Supplement:
Call the copy constructor or the "=" assignment operator overloaded function

                 

  • Callcopy constructor:
    When using"< /span> a>The object of the left operandCopy initialization the object of the right operandpass, Copy constructor will be called in this case, already exists object isright operandand, Object that has not been initializedisThe left operand”, =

                  
  • Tuning=Calculating multiplication function< /span>existence conceptLeft operation number example赋值给 a i=23>Right operation number examplefor this information session,timecitynumber of left and right operationsand” ,=
    This use
Illustration:

                       

                       

---------------------------------------------------------------------------------------------

                  

Optimization 1: "Uninitialized object = built-in type"

                 

  • currentlysame display typein,currently Information below,
    first stepfirst communication Internal category typeConstruction output pieceTime information
    (for Construction function)
                   
  • Second step:Copy again throughtemporary object Construct initializationThe uninitialized object of the left operand
    (Call: a>)Copy constructor
                   
  • Optimization: Constructor + Copy Constructor< /span>The constructor will actually be called only once (Different compilers have different optimizations, here /span> as an example)VS2020These two calls in the compilerConstructor =>

Illustration:

                        

                       

---------------------------------------------------------------------------------------------

                  

Optimization 2: "Call function through anonymous object"

                 

  • currentlysame display typein,currently Details below,
    first stepfirst< a i=9>Initial anonymous example (tuning:constructive function a>)

                   
  • Second stepRe传值传入 Anonymous modeling function
    (tuning:torture construction function )
                   
  • Optimization: Constructor + Copy Constructor< /span>The constructor will actually be called only once (Different compilers have different optimizations, here /span> as an example)VS2020These two calls in the compilerConstructor =>

Illustration:

                        

                       

---------------------------------------------------------------------------------------------

                  

Optimization three: "Call functions through built-in types"

                 

  • currentlysame display typein,currently Information below,
    first stepfirst communication Internal category typeConstruction output pieceTime information
    (for Construction function)
                   
  • Second stepRe传值传入 Time simulation function
    (tuning:torture construction function )
                   
  • Optimization: Constructor + Copy Constructor< /span>The constructor will actually be called only once (Different compilers have different optimizations, here /span> as an example)VS2020These two calls in the compilerConstructor =>

Illustration:

                        

                       

---------------------------------------------------------------------------------------------

                  

Optimization 4: "Uninitialized objects receive temporary objects returned by function values"

                 

  • currentlysame display typein,currently Under the circumstances,
    first step:function< a i=9>Advance return time torture临时对情Advance return ( Training:torture construction function)

                   
  • Second step:returningtime referenceProgressTortureFirst initiation
    (Tuning:torture construction function)
                   
  • Optimization: copy constructor + copy constructor => Copy constructor
    These two calls in the compiler< /span> as an example)VS2020, Here we take Different compilers have different optimizations (The copy constructor will actually be called only once
Illustration:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

Code related to this blog:

Test.cpp file -- C++ file:

//#define _CRT_SECURE_NO_WARNINGS 1

//包含IO流头文件:
#include <iostream>
//展开std命名空间:
using namespace std;

//class A
//{
//public:
//	//全缺省构造函数(默认构造函数):
//	A(int a = 0)
//		//初始化列表:
//		:_a(a)
//	{
//		/*
//		* Date类中成员变量 A _aa 初始化时,
//		* 会调用这个默认构造函数,
//		* 这个默认构造函数再通过初始化列表
//		* 对 _aa 进行初始化
//		*/
//	}
//private:
//	//成员变量:
//	int _a;
//};
//
//
日期类:
//class Date
//{
//public:
//	
//	//Date(int year, int month, int day)
//	//{
//	//	//构造函数体内初始化:
//	//	_year = year;
//	//	_month = month;
//	//	_day = day;
//
//	//	_ref = year; //引用变量
//	//	_n = 1; //const变量
//	//}
//	
//	//Date(int year, int month, int day)
//	//	//初始化列表:冒号开始,逗号分割
//	//	:_year(year) //初始化:年
//	//	,_month(month) //初始化:月
//	//	,_day(day) //初始化:日
//	//	,_ref(year) //初始化:引用变量
//	//	,_n(1) //初始化:const变量
//	//{
//	//	/*
//	//	* 引用变量 和 const变量,
//	//	* 都必须在定义时就进行初始化,
//	//	* 初始化列表就可以解决这个问题
//	//	*/
//	//}
//
//	Date(int year, int month, int day)
//		//初始化列表:冒号开始,逗号分割
//		:_ref(year) //初始化:引用变量
//		,_n(1) //初始化:const变量
//		,_aa(10) //初始化:自定义对象
//		/*
//		* 引用变量 和 const变量,
//		* 都必须在定义时就进行初始化,
//		* 初始化列表就可以解决这个问题
//		* 
//		* 执行到这里,
//		* 剩下的3个成员变量没有在初始化列表中初始化,
//		* 但它们也已经被定义了,只是因为是内置类型,
//		* 编译器会默认给它们一个随机值,
//		* 如果是自定义类型成员变量的话则会去调用
//		* 其默认构造函数
//		* 
//		* 如果该自定义类型没有合适的默认构造函数,
//		*(全缺省构造函数、显式定义无参构造函数、
//		* 编译器默认生成的构造函数)
//		* 只有显式定义的有参构造函数,那么该对象
//		* 的初始化也可以放在初始化列表中。
//		* 就像这里的_aa一样,在初始化列表中,
//		* 直接调用其有参构造函数进行初始化。
//		* 就是在编译器调用其默认构造函数前,
//		* 先在初始化列表中调用其有参构造函数进行初始化
//		* 
//		* 初始化列表中的初始化顺序和
//		* 成员变量声明的顺序是一样的,
//		* 所以建议初始化列表顺序和声明顺序保持一致
//		*/
//	{
//		//构造函数体内初始化:
//		_year = year; //初始化:年
//		_month = month; //初始化:月
//		_day = day; //初始化:日
//	}
//
//private:
//	//声明成员变量,未开空间:
//
//	int _year = 1;
//	int _month = 1;
//	int _day = 1;
//	/*
//	* 这里给的1是缺省值,
//	* 如果 初始化列表 中没有
//	* 对应成员变量的初始化,
//	* 那么该成员变量的值就会是这里设置的缺省值
//	*(这里缺省值的功能就类似初始化列表)
//	*/
//
//	//引用变量:必须在定义时就初始化
//	int& _ref; 
//	//const变量:必须在定义时就初始化
//	const int _n; 
//	//自定义类型对象:
//	A _aa;
//};
//
//
//class Stack
//{
//public:
//	//栈类构造函数:
//	Stack(int n = 2)
//		:_a((int*)malloc(sizeof(int)*n))
//		,_top(0)
//		,_capacity(n)
//		/*
//		* 虽然说尽量使用初始化列表进行初始化,
//		* 但也不是说就完全不在构造函数体中写代码了,
//		* 
//		* 初始化列表中也可以进行动态内存开辟,
//		* 但有些初始化或检查的工作,初始化列表也不能全部搞定,
//		* 想这里就没有办法对开辟的动态空间进行检查
//		*/
//	{
//		//……
//		//构造函数体内:
//
//		//动态空间检查工作:
//		if (_a == nullptr)
//		{
//			perror("malloc fail");
//			exit(-1);
//		}
//
//		//数据拷贝工作:
//		memset(_a, 0, sizeof(int) * n);
//
//		//想这里的两种工作初始化列表就完成不了
//	}
//
//	//……
//private:
//	int* _a;
//	int _top;
//	int _capacity;
//};
//
//class MyQueue
//{
//public:
//	MyQueue(int n1 = 10, int n2 = 20)
//		:_s1(n1)
//		,_s2(n2)
//		//通过初始化列表自己控制自定义的初始化值
//		//不受制于自定义类型中构造函数的缺省值
//	{}
//
//private:
//	Stack _s1;
//	Stack _s2;
//};
//
主函数:
//int main()
//{
//	//实例化对象--定义成员变量:对象整体定义
//	//每个成员变量在 初始化列表 中进行定义
//	Date d1(2023, 10, 31);
//	/*
//	* 对象中的 引用变量(_ref) 和 const变量(_n),
//	* 应该在示例化对象时就已经被定义好了,
//	* 所以我们实例化时不需要传这两个变量的参数,
//	* 要完成这个步骤就需要依靠 初始化列表了
//	*/
//
//	MyQueue(); 
//	MyQueue(100, 1000);
//
//	/*
//	*		总结--初始化列表的作用:
//	* 
//	* 1、解决必须在定义时就要求初始化的类型变量问题
//	* (如:引用类型成员变量、const成员变量、
//	* 自定义类型中只有有参构造函数的初始化)
//	* 
//	* 2、让一些自定义类型的成员变量自己显式控制初始化值
//	* 
//	* 3、尽量使用初始化列表进行初始化,
//	* 初始化列表就是成员变量定义的地方,
//	* 在定义的地方就进行初始化会更好一点,
//	*(80%-100%的工作初始化列表能完成,
//	* 还有一些工作只能在函数体中完成,
//	* 所以要将初始化列表和函数体结合起来使用)
//	*/
//
//	return 0;
//}



//namespace ggdpz 
//{	
//	//定义一个全局变量:
//	int count = 0; //统计一共创建了多少个A对象
//
//	/*
//	* 因为我们完全展开了stdC++标准库,
//	* 且库中有count同名变量,
//	* 所以为了避免命名冲突,
//	* 定义一个命名空间,在命名空间中定义自己的count
//	*/
//}

A类:
//class A 
//{
//public:
//	//构造函数:
//	A() { ++ggdpz::count; }
//
//	//拷贝构造函数:
//	A(const A& t) { ++ggdpz::count; }
//	
//	/*
//	* 当调用了一次构造函数或拷贝构造函数时,
//	* 就说明创建了一个对象,
//	* ++count即创建了一个对象
//	*/
//
//	//析构函数:
//	~A() {	}
//
//private:
//	
//};
//
创建一个函数:
//A func()
//{
//	//创建一个A对象:
//	A aa;
//
//	//返回该对象:
//	return aa;
//}
//
主函数:
//int main()
//{
//	//创建一个A对象:
//	A aa;
//	//调用func()函数:
//	func();
//
//	ggdpz::count++;
//	/*
//	* 但如果使用全局变量,
//	* 我这里也可以直接调用让其+1,
//	* 但我们实际并没有创建对象,
//	* 这时候统计的创建对象个数就是错的了
//	* 
//	* 但如果把count统计变量设置为成员变量的话,
//	* 就可以解决该问题了,
//	*/
//
//	//打印创建了多少个对象:
//	cout << ggdpz::count << endl;
//
//	return 0;
//}


A类:
//class A
//{
//public:
//	//构造函数:
//	A() { ++count; }
//
//	//拷贝构造函数:
//	A(const A& t) { ++count; }
//
//	/*
//	* 当调用了一次构造函数或拷贝构造函数时,
//	* 就说明创建了一个对象,
//	* ++count即创建了一个对象
//	*/
//
//	//析构函数:
//	~A() {	}
//
//	//Get()方法:
//	static int GetCount() //只读不写 
//	{
//		/*
//		* 静态成员函数,没有隐藏的this指针,
//		* 所以没法在函数体中使用非静态成员变量了,
//		*/
//
//		//返回想要获得的count:
//		return count;
//	}
//
//private:
//	//私有成员变量::
//
//	//统计创建对象个数
//	static int count;  //只是声明 
//	/*
//	* 如果是:int count = 0;
//	* 这里虽然是用于统计创建对象个数,
//	* 但是每个对象中都有一个count成员变量,
//	* 而且都是独立的,统计计数时不是加到同一个count上,
//	* 所以起不到统计的作用
//	* 
//	* 所以这里应该使用static进行修饰,
//	* 就可以让被修饰的成员变量能够被共享,
//	* 让该类的所有对象共用该成员变量,
//	* 这样就可以在成员变量上统计创建对象个数了
//	* 
//	* 成员变量使用static进行修饰后就不支持给缺省值了,
//	* 即不能写成:static int count = 0;
//	* 因为这里给的缺省值实际上是给初始化列表的,
//	* 而初始化列表中是初始化某个对象,
//	* static不是对象,不会在初始化列表中执行,
//	* 所以不能这样给缺省值
//	*/
//};
//
///*
//* static成员变量声明是在类中声明,
//* 但实现(定义)是在类外面实现(定义)的,
//* 只能初始化一次,而且其初始化不是在实例化对象时,
//* 而是在执行主函数之前就已经初始化了,
//* 这也是其不能在类中初始化列表中进行初始化的原因
//* 
//* 其实本质还是一个全局变量,只不过被放在类域中了,
//* 是这个类专属的“全局变量”(私有成员变量)
//*/
//int A::count = 0;
//
创建一个函数:
//A func()
//{
//	//创建一个A对象:
//	A aa;
//
//	//返回该对象:
//	return aa;
//}
//
主函数:
//int main()
//{
//	//创建一个A对象:
//	A aa;
//	//调用func()函数:
//	func();
//
//	/*
//	* 此时就不能直接对其进行++了,
//	* 因为是该类专属的“全局变量”,
//	* 属于整个类,属于这个类的所有对象,
//	* 受到了访问限定符的影响(private),
//	* 所以不能直接对static成员变量进行调用
//	*/
//	A::count++;
//
//	/*
//	* 如果static成员变量是共有的(public):
//	* 则可以通过类域直接访问,
//	* 也可以通过对象进行调用,
//	* 但通过对象进行调用实际是找到该对象的类,
//	* 然后再进行访问的
//	*/
//	cout << A::count << endl; //通过类域直接访问
//	cout << aa.count << endl; //通过对象进行调用
//	//aa.count -> aa是A类的对象,count是A类中的 -> A::count
//
//	/*
//	* 如果static成员变量是私有的(private):
//	* 那要怎么获得它呢?这时我们可以定义对应的Get()方法,
//	* 再通过对象调用对应的Get()方法来获取static成员变量,
//	* Get()方法只读不写,只能读不能写
//	*/
//	//通过对应Get()方法获得count的值:
//	cout << aa.GetCount() << endl;
//
//	/*
//	* 但如果static成员变量是私有的,同时又没有对象,
//	* 要怎么获得static成员变量呢?
//	* 
//	* 方式一:为了调用static成员变量而创建一个对象,
//	* 因为是专门为了调用而创建的对象,所以调用时要-1,
//	* 减去创建这个对象的次数(不是很好的方式)
//	* 
//	* 方式二:如果 类型+() 匿名对象调用对应的Get()方法,
//	* 匿名对象的生命周期只有写它的那一行,过了这一行后,
//	* 就会调用析构函数“销毁”匿名对象,匿名对象也会调用构造函数,
//	* 所以如果使用匿名函数查看这里的count的话,同样需要-1
//	* (也不是很好的方式)
//	* 
//	* 方式三:将对应的Get()方法设置为静态成员函数,
//	* 静态成员函数的特点:没有this指针,方式一和方式二中,
//	* 因为Get()方法中有this指针,所以必须通过对象调用this指针
//	* 来使用Get()方法获取count的值。
//	* 而将其设置为静态成员函数没有this指针的话(在类域中),
//	* 调用时就不用依靠对象了,通过类域进行调用即可
//	*/
//	//方式一:通过有名对象
//	A aa; //有名对象
//	cout << aa.GetCount() - 1 << endl; //非静态成员函数
//
//	//方式二:通过匿名对象
//	//这里的 A() 就是匿名对象
//	cout << A().GetCount() - 1 << endl; //非静态成员函数
//
//	//方式三:通过静态成员函数
//	cout << A::GetCount() << endl; //静态成员函数
//	//直接通过A类域调用到对应的Get( )方法
//	cout << A().GetCount() << endl; 
//	/*
//	* 设置成静态成员函数后,依旧可以通过对象来调用,
//	* A()匿名对象不是通过this指针调用Get方法的,
//	* 而是通过A()找到对应类域,再在类域中调用到Get方法的
//	*(静态成员函数只要能找到其对应的类域即可进行调用)
//	*
//	*				   总结:
//	*					一、
//	* 可以将静态成员函数和静态成员变量看成是
//	* “类域中受到了限制的全局函数和全局变量”,
//	* 两者本质没太大的区别,使用sizeof计算有
//	* 静态成员函数或静态成员变量的类时,
//	* 也不会计算类中静态成员函数或变量的大小。
//	*(类的大小不包括静态成员函数或变量)
//	* 使用静态(static)修饰本质是为了和非静态进行区别
//	* 
//	*					二、
//	* 静态成员函数和静态成员变量属于这个类的所有对象,
//	* 而且它们受到类域和访问限定符的限制
//	* 
//	*/
//
//	return 0;
//}


A类(单参数构造函数):
//class A
//{
//public: //共有成员函数:
//
//	//(有参)构造函数:
//	explicit A(int a)
//		//初始化列表:
//		:_a(a)
//	{}
//
//private: //私有成员变量:
//	int _a = 0;
//};
//
日期类(多参数构造函数):
//class Date
//{
//public: //共有成员函数:
//
//	//多参数构造函数:
//	explicit Date(int year, int month = 1, int day = 1)
//		: _year(year)
//		, _month(month)
//		, _day(day)
//	{}
//
//private: //私有成员变量:
//	int _year;
//	int _month;
//	int _day;
//};
//
主函数:
//int main()
//{
//	//正常调用构造函数进行初始化:
//	A aa1(1);
//	A aa2(2);
//
//	/*
//	* 之前了解过的赋值运算符重载,
//	* 其两个操作数都是类类型,
//	* 但如果左操作数是类类型,
//	* 右操作数是内置类型呢:
//	*/
//	A aa3 = 3; //正常赋值:类类型 = 内置类型
//	//左操作数为类类型,右操作数为内置类型
//	/*
//	*		对应构造函数为:A(int i)
//	* 此时就是将内置类型隐式转换成了自定义类型对象,
//	* 涉及到类型转换,那就会产生一个临时变量(具有常属性),
//	* 这里会产生一个 A(3) 的临时对象(通过构造函数产生),
//	* 再对 A(3) 进行拷贝构造到 aa3 (通过拷贝构造函数),
//	* 所以会先调用构造函数,再调用拷贝构造函数,
//	* 之所以支持这个转换,
//	* 是因为A类中有 int类型的单参数构造函数
//	*(单参数构造函数的类型不同支持的隐式转换类型也不同)
//	*/
//
//	const A& ra = 3; //引用:类类型 = 内置类型
//	/*
//	* 这里对象ra引用的是 类型转换时产生的临时对象(变量),
//	* 临时对象(变量)具有常属性,其类型是 const A , 
//	* 所以引用时的类型应该是:const A&
//	*/
//
//	//那么如果不想让类类型接收隐式转换的内置类型该怎么办呢?
//	/*
//	* 在构造函数前加上关键字explicit,
//	* 即在构造函数 A(int a) 前加上变成:
//	* explicit A(int a) ,此时该构造函数就不支持
//	* 类类型接收隐式转换的内置类型了
//	*(加上explicit后的构造函数就不支持隐式类型转换了)
//	*(但显式类型转换还是可以的)
//	*/
//
//	//“特殊”多参数构造函数正常调用初始化:
//	Date d1(2023, 11, 2);
//
//	//类类型 = “(多个内置类型)”
//	Date d2 = (2023, 11, 3);
//	//等价于:Date d2 = 3
//	/*
//	*			对于“特殊”多参数构造函数:
//	* Date(int year, int month = 1, int day = 1)
//	* 
//	* 这里是可以编译过的,但d2的日期是 3/1/1 ,
//	* 因为这里的 (2023,11,3) 其实是逗号表达式,
//	* 只以最后的值为准,即3,3被放在“年”的位置,
//	* 而后面的月和天其实是缺省值,所以结果是 3/1/1
//	* 
//	* 这里的多参数构造函数是半缺省构造函数,
//	* 只有一个“年”必须给初始化值,所以该构造函数
//	* 是支持用一个值进行初始化的。
//	* 
//	* 所以只要一个对象的构造函数能够支持一个值完成初始化,
//	* 就可以通过 类类型 = 内置类型 进行初始化
//	* (构造函数通过缺省值进行调整)
//	*/
//
//	//多参数构造函数:
//	Date d4 = { 2023,11,2 };
//	/*
//	* C++11中是支持类对象多参数初始化的,
//	* 不过使用的是大括号{},列表初始化,
//	* 初始化过程中也有产生临时对象,
//	* 实际执行还是通过构造函数进行初始化的
//	*/
//
//	const Date& d5 = { 2023,11,2 };
//	//证明列表初始化也有产生临时对象,其类型也有常属性
//
//	return 0;
//}


时间类:
//class Time
//{
//	/*
//	* 日期类想访问时间类私有成员变量,
//	* 应在时间类中将日期类设置为友元类
//	* 
//	* 声明日期类为时间类的友元类,
//	* 则在日期类中就可以直接访问Time类中,
//	* 的私有成员变量:
//	*/
//	friend class Date;  //友元类
//	/*
//	* 友元关系是单向的,
//	* 此时Date日期类能访问Time时间类的私有成员变量,
//	* 但Time时间类不能访问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
//{
//	//友元函数声明:
//	//“<<”流运算符重载函数(类中友元声明):
//	friend ostream& operator<<(ostream& _cout, const Date& d);
//	//“>>”流运算符重载函数(类中友元声明):
//	friend istream& operator>>(istream& _cin, Date& d); 
//
//	/*
//	* 友元函数:让函数能够访问类中的私有成员
//	* 
//	* 友元声明虽然写在类中,但它并不是成员函数,
//	* 没有隐藏的this指针,友元函数类中声明类外定义,
//	* 没有指定类域。
//	* 就是一个全局函数,并声明“我是你的友元(“朋友”)”
//	*/
//
//	/*
//	* 友元会让耦合度变高,某种程度上破坏了封装
//	*/
//
//public: //共有成员函数:
//	//全缺省构造函数:
//	Date(int year = 1900, int month = 1, 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; //时间类对象
//};
//
“<<”流运算符重载函数(全局中实现):
//ostream& operator<<(ostream& _cout, const Date& d)
//{
//	_cout << d._year << "-" << d._month << "-" << d._day;
//
//	return _cout;
//}
//
“>>”流运算符重载函数(全局中实现):
//istream& operator>>(istream& _cin, Date& d)
//{
//	_cin >> d._year >> d._month >> d._day;
//
//	return _cin;
//}



A类:
//class A
//{
//private: //私有成员变量(A类):
//    int h; //4个字节
//
//public: //公有成员函数:
//
//    //在A类内部再定义一个B类:
//    class B //内部类B
//    {
//    public: //公有(B类):
//        int _b;
//
//        void func()
//        {
//            A aa;
//            aa.h++; //直接访问外部类的私有成员
//            /*
//            * 因为内部类天生就是外部类的友元,
//            * 所以内部类中可以直接访问外部类的私有成员
//            */
//        }
//    };
//
//    /*
//    * A类和内部类B的关系:
//    * 内部类B可以认为就是一个普通的类,
//    * 只是其会受到 A的类域 和 访问限定符 的限制,
//    * 而且内部类天生就会是外部类的友元(重点)
//    * 
//    * 跟静态成员变量和函数类似,性质跟在全局时一样,
//    * 只是受到了 类域 和 访问限定符 的限制
//    * 
//    * C++中内部类比较少用
//    */
//};
//
主函数:
//int main()
//{
//    //计算拥有内部类的A类的大小:
//    cout << sizeof(A) << endl;
//    /*
//    * 这里A类的大小计算后是4个字节,
//    * 也就是说计算A类时是不会计算内部类B的
//    */
//
//    //可以直接定义A对象:
//    A aa;
//
//    //但无法直接定义内部类B对象:
//    B bb; //爆红
//
//    //指定其类域后就可以定义其对象了:
//    A::B bb; //内部类B的权限为公有
//
//    A::B bb;
//    /*
//    * 指明类域后,但如果内部类B的权限为私有,
//    * 那单单指明类域也无法创建内部类B对象,
//    * 而是只能在类内部进行使用
//    */
//
//    return 0;
//}



//扩展内容:一些构造时的优化(不同编译器优化不同)

//A类:
class A
{
public: //公有成员函数:
	//构造函数(有参):
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	//拷贝构造函数:
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	
	//“=”赋值运算符重载函数:
	A& operator=(const A& aa)
	{
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}

	//析构函数:
	~A()
	{
		cout << "~A()" << endl;
	}

private: //私有成员变量:
	int _a;
};

//主函数:
int main()
{
	A aa1(1); //调用(有参)构造函数

	A aa2(aa1); //调用拷贝构造函数

	//如果有一个已经存在的对象(aa1):
	A aa3 = aa1; //调用拷贝构造函数
	/*
	* 一个已经存在的对象拷贝初始化
	* 另一个要创建的对象 -- 赋值拷贝
	* 
	* 其实就相当于上面的:A aa2(aa1)
	*/
	
	//如果两个都是已经存在的对象(aa2 和 aa3):
	aa2 = aa3; //调用"="赋值运算符重载函数

	return 0;
}


//优化一 -- 主函数:
int main()
{
	//优化一:
	A aa1 = 1; //这里就是以下第三步中的b类型
	/*
	* 第一步:先用1构造一个临时对象(构造函数)
	* 第二步:再通过临时对象拷贝构造出aa1(拷贝构造函数)
	*
	*				 第三步(优化):
	*			  同一个表达式中(注意),
	*	连续构造+构造 / 构造+拷贝构造 / 拷贝构造+拷贝构造
	*			这些操作可能会被合二为一
	*	a、			构造+构造 -> 构造
	*	b、		 构造+拷贝构造 -> 构造
	*	c、	   拷贝构造+拷贝构造 -> 拷贝构造
	*
	* 这里就是b类型,先调用 构造+拷贝构造 ,实际只调用了一次构造函数
	*/

	return 0;
}

//func函数:
void func(A aa)
{}

//主函数:
int main()
{
	A aa1(1); //初始化:调用构造函数

	func(aa1); //传值传参:调用拷贝构造函数
	/*
	* 这里虽然也是先调用构造函数,
	* 再调用拷贝构造函数,但这里不是再同一个表达式中
	*/

	//优化二:
	//通过匿名对象调用函数:
	func(A(2));
	/*
	* 这里需要先初始化匿名对象,
	* 调用构造函数,
	* 再传值传参匿名对象调用func函数,
	* 调用拷贝构造函数
	* 
	* 这里跟上面不同,是在同一个表达式中,
	* 所以 构造 + 拷贝构造 -> 构造函数
	* 合二为一只会调用一次构造函数
	*/

	//优化三(和优化一类似):
	func(3);
	/*
	* 先用3构造一个临时对象,
	* 调用构造函数,
	* 再用临时对象进行传值传参,
	* 调用拷贝构造函数
	* 
	* 这里也是在同一个表达式中,
	* 所以所以 构造 + 拷贝构造 -> 构造函数
	* 合二为一只会调用一次构造函数
	*/

	return 0;
}




A func()
{
	A aa;
	/*
	* 函数中初始化一个A类型对象,
	* 调用构造函数
	*/

	return aa;
	/*
	* 因为是传值返回,所以拷贝出aa的临时对象,
	* (调用拷贝构造函数)
	*/
}

//主函数:
int main()
{
	//优化四:
	A aa1 = func();
	/*
	* 传值返回时调用了一次拷贝构造函数,
	* 再通过返回的临时变量拷贝初始化aa1,
	* (调用拷贝构造函数)
	* 
	* 即传值返回时调用了一次拷贝构造函数,
	* 然后又继续调用一次拷贝构造函数进行
	* 拷贝初始化对象aa1,
	* 所以是连续调用了两次拷贝构造函数
	* (同一个表达式中)
	* 
	* 所以 拷贝构造 + 拷贝构造 -> 拷贝构造
	* 合二为一只会调用一次拷贝构造函数
	*/

	A aa2;
	aa2 = func();
	/*
	* 这里则不会进行优化,
	* 因为这时的“=”两边都是已经初始化了的对象,
	* 所以这里会调用“=”赋值运算符重载函数进行赋值,
	* 所以不会像上面一样被优化
	*/

	return 0;
}

 /*
 *			总结:
 *		再同一个表达式中:
 *		构造 + 构造 -> 构造
 *	 构造 + 拷贝构造 -> 构造
 *	拷贝构造 + 拷贝构造 -> 拷贝构造
 * 现在的编译器基本都会做到
 * 
 * 但一些新的编译器可能还会跨表达式优化,
 * 把中间一些不必要对象也优化掉 -- 跨表达式优化
 * (不同编译器可能不同)
 * 是一种激进优化,会使编译器的维护更加复杂,
 * 可能会使之前的代码在优化后有bug
 */

Guess you like

Origin blog.csdn.net/weixin_63176266/article/details/134696225