Static, friendly, intrinsic: parsing these special elements in C++ and optimizations for object copying

W…Y’s homepage

Code repository sharing

Foreword:

Previously we learned many knowledge points about classes and objects in C++. Today we continue to study classes and objects. Finally, we will summarize some keywords in classes and objects, as well as details that need attention. Full of useful information, we will set off between us.

Table of Contents

static member

Static member variable characteristics

static member function

explicit keyword

friends

friend function

Friend class

inner class

Some compiler optimizations when copying objects

Understand classes and objects again


static member

I believe everyone is familiar with static members. We have encountered this type of keyword when learning C language. Regarding it in C++ classes, he has this concept: class members declared as static are called classes Static members, member variables modified with static are called static member variables; member functions modified with static are called static member functions. Static member variables must be initialized outside the class.

Interview question: Implement a class and count how many class objects are created in the program.

class A
{
public:
A() { + + _count; }
A(const A & amp; t) { + + _scount; }
~A() { --_scount; }
static int GetACount() { return _scount; }
private:
static int _scount;
};
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}

In the above program, we know that whether the constructor or copy constructor is called, a class object will be generated, and calling the destructor will consume a class object. The first thing we thought of was to create a global variable count for counting. But this would be very imprecise, because the global count can be modified arbitrarily. If we create a private variable count in private for counting, it will definitely not work, because every time an object is created, a count will be born.

In response to this problem, C++ proposed the method of statically modifying member variables.

Static member variable characteristics

1. Static members are shared by all class objects and do not belong to a specific object. They are stored in the static area.
2.Static member variables must be defined outside the class. The static keyword is not added when defining. It is just declared in the class

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class A
{
public:
A() { + + _count; }
A(const A & amp; t) { + + _scount; }
~A() { --_scount; }
static int GetACount() { return _scount; }
//private:
static int _scount;
};
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
int main()
{
A::_scount + + ;
cout << A::_scount << endl;
TestA();
cout << A::_scount << endl;
return 0;
}

If we set the static member variable as public, we can access it in the function without writing a special function in the class for access.
3. Class static members can be accessed using class name::static member or object.static member

class A
{
public:
A() { + + _count; }
A(const A & amp; t) { + + _scount; }
~A() { --_scount; }
static int GetACount() { return _scount; }
//private:
static int _scount;
};
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
int main()
{
A aa;
cout << &A::_scount << endl;
cout << & amp;aa._scount << endl;
return 0;
}

We can Use :: to access static member variables, or you can use object.static members to access. The meaning of these two is the same. aa just declares the static member variables in the class and has no other practical meaning. The resulting address is the same.

Static member function

When we call a private member, we usually write a function to access it.

class A
{
public:
A() { + + _count; }
A(const A & amp; t) { + + _scount; }
~A() { --_scount; }
    int GetACount() { return _scount; }
private:
static int _scount;
};
int A::_scount = 0;
int main()
{
A aa;
cout << aa.GetACount() - 1 << endl;
return 0;
}

We want to know how many class objects there are and create a class to call the function for summation, so we need to perform a -1 operation when getting the result. Such an operation is very time-consuming, and we sometimes forget to perform such an operation, resulting in incorrect results, so we can also use anonymous objects.

class A
{
public:
A() { + + _count; }
A(const A & amp; t) { + + _scount; }
~A() { --_scount; }
    int GetACount() { return _scount; }
private:
static int _scount;
};
int A::_scount = 0;
int main()
{
//A() is called an anonymous object, and its life cycle is only in this line
cout << A().GetACount() << endl;
return 0;
}

It will directly call the destructor to destroy the anonymous object after using it. Unlike the created object, the object is destroyed at the end of the program after creation.

But neither of these methods is the best way to write it. The best way to write it is to use static member functions:

class A
{
public:
A() { + + _count; }
A(const A & amp; t) { + + _scount; }
~A() { --_scount; }
static int GetACount() { return _scount; }
private:
static int _scount;
};
int A::_scount = 0;
int main()
{
//A() is called an anonymous object, and its life cycle is only in this line
    cout << A::GetACount() << endl;
cout << A().GetACount() << endl;
return 0;
}

Static member functions do not have a hidden this pointer and cannot access any non-static members, so we can directly access them with ::, but is it okay to use the previous method? It is also possible, because the conditions provided by us when calling the function are greater than the conditions required for function investigation! ! !

Summary: Static member variables and static member functions are essentially similar to global variables and global functions, but they are exclusive to a certain class and are modified by class domains and access qualifiers!

Niuke OJ practiceicon-default.png?t=N7T8https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13 & amp;tqId=11200 & amp;tPage=3 & amp;rp=3 & amp;ru=/ta/coding-interviews & amp;qru=/ta/coding-interviews/question-ranking The above questions can well reflect the role of static.

code show as below:

class countt
{
public:
    countt()
    {
        _ret + = _i;
         + + _i;
    }
    static int GetRet()
    {
        return _ret;
    }
private:
    static int _i;
    static int _ret;
};
int countt::_i = 1;
int countt::_ret = 0;
class Solution {
public:
    int Sum_Solution(int n) {
        countt arr[n];
        return countt::GetRet();
    }
};

explicit keyword

In the last blog we learned about the initialization list, then we can have multiple initialization methods in the constructor.

class A {
public:
A(int a)
:_a(a)
{
\t\t
}
private:
int _a;
};
int main()
{
A aa1(1);
A aa2();
A aa3 = 3;
return 0;
}

We are all very familiar with the first two methods of calling A aa1(1) and A aa2(), but what is the third method? The third method is to implicitly convert the built-in types into custom types. Just like converting a variable of type int to double. Their principle is to form a temporary variable for copy construction and then assign it.

However, implicit conversion is conditional. It must be supported by a single-parameter constructor or a semi-default function that passes one parameter. Without its corresponding constructor, the built-in type cannot be converted to a custom type.

class A {
public:
A(int a)
:_a(a)
{
\t\t
}
private:
int _a;
};
int main()
{
int* p = nullptr;
A aa = p;
return 0;
}

If we don’t want to convert it, C++ provides a keyword explicit to limit implicit type conversion.

class A {
public:
explicit A(int a)
:_a(a)
{

}
private:
int _a;
};
int main()
{
A aa3 = (A)3;
return 0;
}

But we can use forced type conversion, so the explicit keyword only limits implicit conversion! ! !

class Date
{
public:
    explicit Date(int year)
:_year(year)
{}
explicit Date(int year, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}

private:
int _year;
int _month;
int _day;
};
voidTest()
{
Date d1(2022);
Date d2 = 2023;
//C++98 does not support, C++11 supports implicit conversion of multiple parameters:
Date d3 = {2023, 11, 5};

return 0;
}

Friendly Yuan

Friends provide a way to break through encapsulation and sometimes provide convenience. However, friends will increase coupling and destroy encapsulation, so
You Yuan should not be used too much.
Friends are divided into: friend functions and friend classes

Friend function

Problem: Now I try to overload operator<<, and then I find that there is no way to overload operator<< into a member function. Because of cout
The output stream object and the implicit this pointer preempt the position of the first parameter. The this pointer defaults to the first parameter, which is the left operation
Counted. However, in actual use, cout needs to be the first formal parameter object to be used normally. So operator<< needs to be overloaded into
Global functions. But it will also cause members outside the class to be unable to access. At this time, friends are needed to solve the problem. operator>>Similarly.

class Date
{
public:
Date(int year, int month, int day)
  : _year(year)
  , _month(month)
  , _day(day)
{}
// d1 << cout; -> d1.operator<<( & amp;d1, cout); does not conform to conventional calls
// Because the first parameter of the member function must be hidden this, d1 must be placed on the left side of <<
ostream & amp; operator<<(ostream & amp; _cout)
{
  _cout << _year << "-" << _month << "-" << _day << endl;
  return _cout;
}
private:
int _year;
int _month;
int _day;
};

Friend functions can directly access private members of a class. They are ordinary functions defined outside the class and do not belong to any class, but they need to be
For internal declaration of a class, you need to add the friend keyword when declaring.

class Date
{
friend ostream & amp; operator<<(ostream & amp; _cout, const Date & amp; d);
friend istream & amp; operator>>(istream & amp; _cin, Date & amp; d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream & amp; operator<<(ostream & amp; _cout, const Date & amp; d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream & amp; operator>>(istream & amp; _cin, Date & amp; d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
int main()
{
Date d;
cin >> d;
cout << d << endl;
return 0;
}

illustrate:

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 and are not restricted by class access qualifiers.
A function can be a friend function of multiple classes
The principle of calling friend functions is the same as that of ordinary functions.

Friend class

All member functions of a friend class can be friend functions of another class and can access non-public members of another class.

Friendship is one-way and not exchangeable.
For example, in the above Time class and Date class, if the Date class is declared as its friend class in the Time class, then it can be directly used in the Date class.
You can access the private member variables of the Time class, but you cannot access the private member variables of the Date class in the Time class.
Friend relationships cannot be transferred
If C is a friend of B and B is a friend of A, it cannot mean that C is a friend of A.
Friend relationships cannot be inherited. I will give you a detailed introduction to the inheritance position.

class Time
{
 friend class Date; // Declare the date class as a friend class of the time class, then directly access the Time class in the date class
Private member variables in
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)
 {
   // Directly access private member variables of the time class
   _t._hour = hour;
   _t._minute = minute;
   _t._second = second;
 }
private:
 int _year;
 int _month;
 int _day;
Time_t;
};

inner class

Concept: If a class is defined inside another class, this inner class is called an inner class. An inner class is an independent class,
It does not belong to the outer class, and it cannot access the members of the inner class through the object of the outer class. Outer classes have no advantage over inner classes
access rights.

Note: The inner class is the friend class of the outer class. See the definition of friend class. The inner class can be accessed through the object parameters of the outer class.
Ask all members in the outer class. But the outer class is not a friend of the inner class. So B is a friend of A, and B is an ordinary class, only restricted by A’s class domain and access qualifier.

class A
{
private:
int h;
public:
class B
{
public:
int _b = 1;
};
};
int main()
{
A a;
A::B b;
return 0;
}

characteristic:
1. Inner classes can be defined as public, protected, or private in external classes.
2. Note that the inner class can directly access the static members in the outer class without the need for the object/class name of the outer class.

Extension: C++ inner classes can access private members of outer classes. If you want to access non-static members, you need to pass a pointer to the outer class object through an object reference!

class OuterClass {
private:
  int num = 100;
public:
  void display()
    cout << “OuterClass num:”<< num << endl;
  
  class InnerClass {
  public:
    void accessOuter(OuterClass & obj)
      cout << "InnerClass access OuterClass num:"<< obj.num << endl;
  };
};
int main() {
  OuterClass obj1;
  obj1.display();
  OuterClass::InnerClass obj2;
  obj2.accessOuter(obj1);
  return 0;
}

3. sizeof (external class) = external class, has nothing to do with internal classes.

class A
{
private:
int h;
public:
class B // B is naturally a friend of A
{
public:
int _b = 1;
};
};
class C
{
private:
int h;
};
class D
{
private:
int _d = 1;
C_c;
};
int main()
{
cout << sizeof(A) << endl;
cout << sizeof(D) << endl;
return 0;
}

So don’t get confused.

Some compiler optimizations when copying objects

During the process of passing parameters and returning values, the compiler will generally do some optimizations to reduce the copying of objects. This may not work in some scenarios.
is very useful.

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 & amp; aa)" << endl;
}
A & amp; operator=(const A & amp; aa)
{
cout << "A & amp; operator=(const A & amp; aa)" << endl;
if (this != & amp;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()
{
// Pass parameters by value
A aa1;
f1(aa1);
cout << endl;
// Return by value
f2();
cout << endl;
//Implicit type, continuous construction + copy construction->optimized to direct construction
f1(1);
// In an expression, continuous construction + copy construction -> optimized to one construction
f1(A(2));
cout << endl;
// In an expression, continuous copy construction + copy construction -> optimize a copy construction
A aa2 = f2();
cout << endl;
// In an expression, continuous copy construction + assignment overloading -> cannot be optimized
aa1 = f2();
cout << endl;
return 0;
}

Understand classes and objects again

Physical computers in real life do not recognize it. Computers only recognize data in binary format. If you want the computer to recognize the current
To describe the entities in real life, users must describe the entities through some object-oriented language, and then create programs by writing programs.
The computer can only recognize the object after it is created. For example, if you want the computer to recognize the washing machine, you need to:
1. Users must first abstract the reality of the washing machine entity – that is, understand the washing machine at the level of human thinking. What are the functions of the washing machine?
What attributes and functions does it have? It is a process of abstract cognition of the washing machine.
2. After 1, people already have a clear understanding of the washing machine in their minds, but the computer is not clear yet at this time.
Chu, if you want the computer to recognize the washing machine in your imagination, you need to use some object-oriented language (such as: C++,
Java, Python, etc.) describe the washing machine using classes and input them into the computer.
3. After 2, there is a washing machine class in the computer, but the washing machine class only controls laundry from the perspective of the computer.
Machine objects are described. Through the washing machine class, specific washing machine objects can be instantiated. At this time, the computer can
What is a washing machine?
4. Users can use the washing machine object in the computer to simulate the actual washing machine entity.
In the class and object stage, everyone must realize thata class describes a certain type of entity (object) and describes the characteristics of the object.
After the description of these attributes and methods is completed, a new custom type is formed, and this custom type can be instantiated.
specific object.

The above is the entire content of this class object, thank you for watching! ! !