<C++> Classes and Objects (Part 2)

1. const member

constA member is a member (variable or function) declared in a class whose value or behavior is unmodifiable throughout the lifetime of the object. This helps to ensure that certain properties or behavior of an object cannot be changed accidentally, thus improving the reliability and maintainability of the code.

There are two cases of using in C++ const: constmember variables and constmember functions.

const member variables:

This is a member variable declared as in the class const. Once the object is created, the values ​​of these variables cannot be modified. constMember variables must be initialized in the member initialization list of the class's constructor.

class MyClass {
    
    
public:
    MyClass(int value) 
        : myConstValue(value) {
    
    }
    int getValue() const {
    
     
        return myConstValue; 
    }

private:
    const int myConstValue;
};

const member functions:

constA member function promises not to modify any of the object's non- mutablemember variables and can constbe called on the object. This allows constread-only operations to be performed on the object. constTo modify a member function of a class, actually modify the implicit thispointer of the member function, indicating that any member of the class cannot be modified in the member function.

class MyClass {
    
    
public:
	int getValue() const {
    
     return myValue; } // 不修改成员变量
	void setValue(int value)const {
    
     myValue = value; } // err 不能修改成员变量

private:
	int myValue;
};

It should be noted that mutablekeywords can be used to modify member variables, even in constmember functions, these variables marked as mutablecan still be modified.

class MyClass {
    
    
public:
    int getValue() const {
    
     return myValue; } // 不修改成员变量
    void setValue(int value) const {
    
     myValue = value; } //即使是const成员函数,也可以修改mutable成员变量
private:
    mutable int myValue;
};

The permission problem of const member

question:

  1. Can a const object call a non-const member function? Answer: No. Because the member functions of const objects are const, and the non-const member functions are not const, the two do not match. If forced to call, it will cause compilation error.
  2. Can a non-const object call a const member function? Answer: Yes. Because a member function of a non-const object can be either const or non-const, the two match.
  3. Can const member functions call other non-const member functions? Answer: No. Because const member functions cannot modify the state of the object, non-const member functions may modify the state of the object, which will cause compilation errors.
  4. Can non-cosnt member functions call other cosnt member functions? Answer: Yes. Because a non-cosnt member function can be both cosnt and non-cosnt, both match.

2. static members

Static members are members of a class that are related to the class itself, not to an instance (object) of the class. Static members are shared among all instances of a class and can be accessed by the class name without creating an instance of the class . There are two types of static members in C++: static data members and static member functions .

static data member

This is a member variable of the class, which is shared among all instances of the class. Static data members are declared in the class's declaration static, but they need to be defined and initialized outside the class . Typically, this is to ensure that all instances of the class share the same data.

class MyClass {
    
    
private:
    static int staticVar;  // 静态数据成员的声明
};

int MyClass::staticVar = 0;  // 静态数据成员的定义和初始化

int main() {
    
    
    MyClass obj1;
    MyClass obj2;

    obj1.staticVar = 5;
    std::cout << obj2.staticVar;  // 输出为 5,因为静态数据成员在所有实例之间共享
    return 0;
}

Note: Although the static member variable staticVar is private here, we directly access it outside the class by breaking through the class domain. This is a special case and is not restricted by access qualifiers, otherwise there is no way to define and initialize static member variables.

static member function

Static member functions are associated with a class, but do not manipulate instance data of the class, so they do not have an implicit thispointer. Static member functions can be called directly by the class name without creating an object of the class. Static member functions do not have a hidden this pointer and cannot access any non-static members . Typically, they are used to perform class-related operations that do not require access to instance-specific data.

class Test {
    
    
public:
    static void Fun() {
    
    
        cout << _a << endl;//error不能访问非静态成员
        cout << _n << endl;//correct
    }

private:
    int _a;       //非静态成员
    static int _n;//静态成员
};

Tips : Classes with static member variables generally contain a static member function for accessing static member variables. (Ordinary member functions can also access static members)

Ways to access static member variables

When the static member variable is public, there are the following access methods:

class Test {
    
    
public:
    static int _n;//公有
};
// 静态成员变量的定义初始化
int Test::_n = 0;

int main() {
    
    
    Test test;
    cout << test._n << endl;  //1.通过类对象突破类域进行访问
    cout << Test()._n << endl;//3.通过匿名对象突破类域进行访问
    cout << Test::_n << endl; //2.通过类名突破类域进行访问
    return 0;
}

When a static member variable is private, there are the following access methods:

class Test {
    
    
public:
    static int GetN() {
    
    
        return _n;
    }

private:
    static int _n;
};
// 静态成员变量的定义初始化
int Test::_n = 0;

int main() {
    
    
    Test test;
    cout << test.GetN() << endl;  //1.通过对象调用成员函数进行访问
    cout << Test().GetN() << endl;//2.通过匿名对象调用成员函数进行访问
    cout << Test::GetN() << endl; //3.通过类名调用静态成员函数进行访问
    return 0;
}

Static members, like ordinary members of a class, also have three access levels: public, private, and protected , so when a static member variable is set to private, even if we break through the class domain, we cannot access it.

Summarize:

  1. Static members are shared by all class objects , do not belong to a specific object, and are stored in the static area
  2. Static member variables must be defined outside the class , the static keyword is not added when defining, and only declared in the class
  3. Class static members can be accessed with class name::static member or object.static member
  4. Static member functions do not have a hidden this pointer and cannot access any non-static members
  5. Static members are also members of a class, subject to public, protected, private access qualifiers

Pay attention to distinguishing two questions:
1. Can a static member function call a non-static member function?
2. Can a non-static member function call a static member function?
Question 1 : No. Because the first formal parameter of a non-static member function defaults to the this pointer, and there is no this pointer in a static member function, a static member function cannot call a non-static member function.
Question 2 : Yes. Because both static member functions and non-static member functions are in the class, they are not restricted by the access qualifier in the class.

3. Tomomoto

Friends provide a way to break out of encapsulation, and sometimes convenience. But friends will increase the degree of coupling and destroy the encapsulation, so friends should not be used more often.

Friends are divided into: friend function and friend class

friend function

A friend function is a special type of function that is allowed to access private members of a class even though the functions are not members of the class. friendA friend function is usually declared with a keyword in the class declaration and has access to private and protected members of the class it is declared a friend of. This mechanism provides a certain level of encapsulation breakage, but sometimes it is necessary, for example to implement operator overloading or optimization in some special cases.

Example:

class Date {
    
    
    // 友元函数的声明
    friend ostream &operator<<(ostream &out, const Date &d);
    friend istream &operator>>(istream &in, Date &d);

public:
    Date(int year = 0, int month = 1, 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 << endl;
    return out;
}
// >>运算符重载
istream &operator>>(istream &in, Date &d) {
    
    
    in >> d._year >> d._month >> d._day;
    return in;
}

Note: where cout is a global object of the ostream class, cin is a global variable of the istream class, and the overloaded functions of the << and >> operators have return values ​​in order to realize continuous input and output operations.

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 the class definition, not restricted by 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

friend class

A friend class means that a class can declare another class as its friend, thereby allowing the friend class to access the private and protected members of the class that declares it as a friend. This can be useful in some special cases, but it needs to be used with caution because it can break the encapsulation of the class.

Example:

class FriendClass {
    
    
public:
    void display(const class MyClass& obj);
};

class MyClass {
    
    
private:
    int privateData;

public:
    MyClass(int data) : privateData(data) {
    
    }

    // 声明 FriendClass 为友元类
    friend class FriendClass;
};

// 在 FriendClass 中可以访问 MyClass 的私有成员
void FriendClass::display(const MyClass& obj) {
    
    
    std::cout << "FriendClass accessing private data: " << obj.privateData << std::endl;
}

int main() {
    
    
    MyClass obj(42);
    FriendClass friendObj;
    friendObj.display(obj);
    return 0;
}

Friend class description :

1. The friendship relationship is one-way and not exchangeable.
 For example, in the above code, B is a friend of A, so the private member variables of class A can be directly accessed in class B, but the private member variables of class B cannot be accessed in class A.
2. The friendship relationship cannot be transmitted.
 If A is a friend of B and B is a friend of C, it cannot be deduced that A is a friend of C.

3. The friendship relationship does not have inheritance , that is, if class A is a friend of class B, the subclass of class B will not automatically become a friend of class A.

4. Inner classes

An inner class is a class defined inside another class. That is, a class can declare and define a class inside another class, and the declared class is called an inner class. Inner classes can access members of the outer class, including private members, because they are considered friends of the outer class .

Note :
 1. At this time, the inner class is an independent class, it does not belong to the outer class, and the inner class cannot be called through the object area of ​​the outer class.
 2. Outer classes do not have any privileged access to inner classes.
 3. The inner class is the friend class of the outer class, that is, the inner class can access all members of the outer class through the object parameters of the outer class. But the outer class is not a friend of the inner class.

Features :
 1. The inner class can be defined in any of the three areas of public, private and protected of the outer class.
 2. The inner class can directly access the static and enumeration members in the outer class, without the object/class name of the outer class.
 3. The size of the outer class has nothing to do with the size of the inner class.

class A//外部类
{
    
    
public:
    class B//内部类
    {
    
    
    private:
        int _b;
    };

private:
    int _a;
};

int main() {
    
    
    cout << sizeof(A) << endl;//外部类的大小:4
    return 0;
}

Here the size of the outer class A is 4, independent of the size of the inner class.

5. Anonymous objects

A C++ anonymous object is a temporary object created without being named. They are typically used in the following three scenarios:

  • Pass parameters to the function by value, for example, Cat();an anonymous object will be generated, and it will be destroyed after execution.
  • Type conversion, such as A a = 11;equivalent A a = A(11);, here A(11)is an anonymous object.
  • When a function needs to return an object, for example, return temp;an anonymous object will be generated first and then returned to the caller.

The lifetime of an anonymous object depends on whether other objects receive it. If there is, then the life cycle of the anonymous object becomes the life cycle of the receiving object; if not, the anonymous object will be destroyed immediately after use.

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;
}

6. Some optimizations when copying objects

In the process of passing parameters and returning values, generally the compiler will do some optimizations to reduce the copying of objects, which is still very useful in some scenarios.

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;
};

void func1(A aa) {
    
    
}

void func2(const A &aa) {
    
    
}

int main() {
    
    
    A aa1 = 1;  // 构造+拷贝构造 -> 优化为直接构造
    func1(aa1); // 无优化
    func1(2);   // 构造+拷贝构造 -> 优化为直接构造   创建一个临时对象,调用构造函数,赋值调用拷贝构造
    func1(A(3));// 构造+拷贝构造 -> 优化为直接构造   A(3)构造  传参拷贝构造

    // 加引用就没有优化了,因为引用是别名,没有拷贝
    func2(aa1);
    func2(2);// 如果func2的参数不是const类型,会出现报错。aa1会创建一个临时对象,这个对象具有常属性,所以func2的参数要设置为const
    func2(A(3));
    return 0;
}
A func3() {
    
    
    A aa;     //构造
    return aa;//拷贝构造
}

A func4() {
    
    
    return A();
}

int main() {
    
    
    func3();//不会优化,因为函数体里面有多个表达式,编译器无法优化

    A aa1 = func3();//构造 + 拷贝构造 + 拷贝构造 -> 优化为一个拷贝构造
    func4();        //构造+拷贝构造 -- 优化为拷贝构造
    A aa2 = func4();//构造 + 拷贝构造 + 拷贝构造 -> 优化为一个拷贝构造
    return 0;
}

Object returns summary:

1. Receive the return value object, try to receive it in the copy construction method, do not assign it (assignment means that the object has already been defined, and the compiler cannot optimize it during assignment)

2. When returning an object in a function, try to return an anonymous object

Summary of function parameter passing: try to use const & parameter passing

Guess you like

Origin blog.csdn.net/ikun66666/article/details/132503843