Table of contents
First, talk about the constructor
1.1 Constructor body assignment
1.23 Reference member variables
1. 24 The "pit" of the initialization list
3. 3 Inner classes (understand)
Fourth, anonymous objects (understand)
Five, some optimizations of the compiler for copying objects
1. Passing parameters by value
4. In an expression, continuous construction + copy construction -> optimized to a construction
6. In an expression, continuous copy construction + assignment overload -> cannot be optimized
First, talk about the constructor
1.1 Constructor body assignment
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
Here's the past member initialization, which is not appropriate in this scenario:
#include<iostream>
using namespace std;
class Time
{
public:
Time(int hour = 10) // 对于在函数体中初始化,必须写缺省参数,否则编译报错。
{
_hour = hour;
}
private:
int _hour;
};
class Date
{
public:
Date(int year, int hour)
{
_year = year;
Time t(hour);
_t = t;
}
private:
int _year;
Time _t;
};
int main()
{
Date a(2023,2);
return 0;
}
1. 2 Initialization list
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
// 成员变量声明
int _year;
int _month;
int _day;
};
- reference member variable
- const member variable
- A custom type member (and the class has no default constructor)
1.21 Custom type members
Use the initialization list to complete, custom type initialization, see the following code:
#include<iostream>
using namespace std;
class Time
{
public:
Time(int hour = 11) // 区别于函数内构造(见1.1代码,这里必须加),全缺省参数可加可不加。
{
_hour = hour;
}
private:
int _hour;
};
class Date
{
public:
Date(int year, int hour)
:_t(hour)
{
_year = year;
}
private:
int _year = 100; // 缺省值,c++11打了个补丁,在内置类型初始化没有给值值时,给缺省值。
Time _t;1
};
int main()
{
Date a(2023,2);
return 0;
}
So we can see the combined internal initialization of the function body and list initialization:
1. If it is an internal initialization of the function, it still needs to be initialized before the assignment, and then assigned, several initializations are required in the middle, which is cumbersome.
2. If you initialize in the initialization list, you can directly display the initialization without having a default constructor with all defaults.
in conclusion:
- It is recommended to use list initialization for custom type members , and constructors without all default parameters must use list initialization.
- The built-in type is recommended to be written in the initialization list, or it can be written in the function body, both are optional. (Unless for the code to look good, it needs to be written as an internal initialization of the function)
1.22 const member variables
The reason is such as: const int x must be initialized where it is defined. (This is how I understand it, it is related to the permission nature of the const xx type )
class Date
{
public:
Date(int year, const int x)
: _z(x)
{
_year = year;
}
private:
int _year;
const int _z;
};
int main()
{
int x = 0; // 用const修饰,就是想等传参;不修,则是缩小权限传参。
Date a(2023, x);
return 0;
}
1.23 Reference member variables
A reference is an "alias" for another variable, and its nature is that modification is not allowed, so it must be initialized when it is defined.
class Date
{
public:
Date(int year, int& x)
: _z(x)
{
_year = year;
}
private:
int _year;
int& _z;
};
int main()
{
int x = 0;
Date a(2023, x);
return 0;
}
1. 24 The "pit" of the initialization list
See what happens with the following code:
class A
{
public:
A(int a)
:_a1(a)
,_a2(_a1)
{}
void Print() {
cout<<_a1<<" "<<_a2<<endl;
}
private:
int _a2;
int _a1;
};
int main() {
A aa(1);
aa.Print();
}
/*A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值*/
The result is D:
Analysis: The order in which member variables are declared in the class is the order in which they are initialized in the initialization list , regardless of their order in the initialization list.
1. 3 explicit keyword
First let's look at the following code:
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year)
{
_year = year;
}
private:
int _year;
};
void test()
{
Date x(10); // 直接调用构造函数
Date a = 100; // (隐式转化int->Date类型)构造一个Date类型的临时变量 +
//拷贝构造 + 优化(a的拷贝构造无法查看) —> 直接调用构造函数
}
( Note: Regarding the compiler's optimization of copying, we will talk about it later in this section)
And this time the explicit keyword is used to modify the constructor, the function is: prohibit type conversion.
explicit Date(int year)
{
_year = year;
}
So Date a = 100, an error is reported
Two, static members
2.1 Concept
Usage scenario: Static modifies global variables, which can be accessed arbitrarily outside the class, so how to design a global variable that can only accessed by the class ?
For example: interview questions: implement a class, and calculate how many class objects are created in the program.
class A
{
public:
A(int i)
:_i(i)
{
_scout++;
}
static int Getscout() // 类外无法取得static 成员, 所以需要一个类成员函数取得。
{
return _scout; // 在静态区中寻找_scout
}
A(A& k)
{
_i = k._i;
_scout++;
}
private:
static int _scout; // 声明。 (注意:缺省值为初始化列表提供的,而static成员是在类外定义)
int _i = 0;
};
int A::_scout = 0; // 初始化, A::通过类域定义----目的是突破类的限制
int main()
{
A a(1);
A b(2);
A c(3);
// 静态成员公有
//cout << c._scout << endl;
//cout << A::_scout << endl; // 两种方式并非去访问类,而是为了突破类域,去静态区去寻找
// 静态成员私有
cout << c.Getscout() << endl; // 这里访问成员函数,然后在静态区中寻找静态成员。
cout << A::Getscout() << endl; // 通过类域访问static成员
}
2.2 Features
Three, Friends
3. 1 Friend function
Function: The friend function can directly access the private members of the class . It is an ordinary function defined outside the class and does not belong to any class, but it needs to be declared inside the class, and the friend keyword needs to be added when declaring.
Features:
- Friend functions can access private and protected members of a class, but not member functions of the class.
- Friend functions cannot be modified with const .
- Friend functions can be declared anywhere in a class definition, regardless of class access qualifiers.
- A function can be a friend function of multiple classes .
- The principle of calling a friend function is the same as that of a normal function.
Use as follows:
class A
{
public:
friend int func(const A& a); // 友元声明
private:
int _i;
};
int func(const A& a) // 普通函数
{
return a._i;
}
3. 2 Friend class
Features:
- 1. All member functions of a friend class can be friend functions of another class, and can access non-public members of another class.
- 2. Friendship cannot be passed. (If C is a friend of B and B is a friend of A , it cannot be said that C is a friend of A. )
- 3. The friendship relationship cannot be inherited, and I will give you a detailed introduction when it is inherited .
- 4. The friendship relationship is one-way and not exchangeable. ( For example, the following Time class and Date class, declare the Date class as its friend class in the Time class, then you can directly
Access the private member variables of the Time class, but you can't access the private member variables in the Date class in the Time class. )
Here is the practice of point 4:
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
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 = 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; // 在Date中声明一个Time类,过去是无法在Time类外直接访问其私有成员。
};
3. 3 Inner classes (understand)
Java is used more, and C++ is used less, here is only for understanding.
3.31 Concept
Use a piece of code to test its internal and external class relationships:
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}
class Time
{
public:
Time(int hour = 0, int minute = 0, int secoud = 0)
:_hour(hour)
, _minute(minute)
, _secoud(secoud)
{}
void func(Date& _d) // 内部内类,Date天生是Time的友元类,所以可以通过对象直接访问Date其内部私有成员。
{
_d._year = _hour;
_d._month = _minute;
_d._day = _secoud;
cout << _d._year << endl;
cout << _d._month << endl;
cout << _d._day << endl;
}
private:
int _hour;
int _minute;
int _secoud;
};
void fundate()
{
cout << _t._hour; // 向内部类直接访问私有失败
}
private:
int _year ;
int _month ;
int _day ;
};
int main()
{
Date::Time b;
Date z;
b.func(z);
return 0;
}
class A
{
public:
class B
{
public:
void func()
{
cout << z << endl; // 访问外部类中的静态变量
}
private:
int _b = 100;
};
private:
int _i = 10;
static int z;
};
int A::z = 10;
int main()
{
A::B a;
a.func();
}
class A
{
public:
class B
{
public:
private:
int _b;
};
private:
int _i;
};
int main()
{
cout << sizeof(A); //运行可知为4字节
}
Fourth, anonymous objects (understand)
For example, let's look at the following code:
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
};
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
class Solution {
public:
int Sum_Solution(int n) {
//...
return n;
}
};
int main()
{
A aa1;
// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
//A aa1();
// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
A();
A aa2(2);
// 匿名对象在这样场景下就很好用,当然还有一些其他使用场景,这个我们以后遇到了再说
Solution().Sum_Solution(10);
return 0;
}
Five, some optimizations of the compiler for copying objects
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "构造" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "拷贝构造" << endl;
}
A& operator=(const A& aa)
{
cout << "赋值构造" << endl;
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
void f1(A aa)
{}
A f2()
{
A aa;
return aa;
}
int main()
{
//1. 传值传参
A aa1;
f1(aa1);
cout << endl;
//2. 传值返回
f2();
cout << endl;
//3. 隐式类型,连续构造+拷贝构造->优化为直接构造
f1(1);
//4. 一个表达式中,连续构造+拷贝构造->优化为一个构造
f1(A(2));
cout << endl;
//5. 一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
A aa2 = f2();
cout << endl;
//6. 一个表达式中,连续拷贝构造+赋值重载->无法优化
aa1 = f2();
cout << endl;
return 0;
}
1. Passing parameters by value
result:
2. Return by value
class A
{
public:
A()
{
cout << "构造" << endl;
}
A(const A& z)
{
cout << "拷贝构造" << endl;
}
~A()
{
cout << "析构" << endl;
}
private:
int _a = 1;
};
A func()
{
A a; // 1次构造
return a; // 1次拷贝构造
}
int main()
{
A k = func(); // 1次拷贝构造,但是编译器会优化,把2次拷贝构造优化为1次
return 0;
It can be seen that 1 construction, 1 copy; but why this is the case, we explain it with the following picture:
3. Implicit typing
result:
4. In an expression, continuous construction + copy construction -> optimized to a construction
result:
5. In an expression, continuous copy construction + copy construction -> optimize a copy construction
result:
6. In an expression, continuous copy construction + assignment overload -> cannot be optimized
Summary: Code optimization is a function of the compiler. The code optimization in the release version is stronger than that in the debug version. At the same time, different compilers have different degrees of optimization.
epilogue
This section is over here, thank you friends for browsing, if you have any suggestions, welcome to comment in the comment area; if you bring some gains to your friends, please leave your likes, your likes and concerns will become bloggers The driving force of the master's creation .