Private and public
There are methods and member variables in a class. After the public keyword is identified, the methods and variables under public become public functions. After the private keyword is identified, the methods and member variables under the private keyword become private. By default, if public is not declared, all methods and members in the class are private. If private is not declared, all methods and members in struct are public.
Friends
In the previous article, we will print, read and other non sales_ The global function of the data class is declared as sales_ The friend function of the data class, so print and read can access sales_ Private member of the data class. Let's recall sales again_ Data class.
class Sales_data { public: //The default construction is implemented through default // Sales_data() = default; //Show implementation default constructs Sales_data() : bookNo(""), units_sold(0), revenue(0.0) {} // copy structure, according to Sales_data type object constructs a new object Sales_data(const Sales_data &sa); Sales_data(const std::string &s) : bookNo(s) {} Sales_data(const std::string &s, unsigned n, double p) : bookNo(s), units_sold(n), revenue(p * n) {} Sales_data(std::istream &is); //Return book number std::string isbn() const { return bookNo; } //Get average unit price double avg_price() const; //Put a sales_ Merge data object into current class object Sales_data &combine(const Sales_data &); friend std::ostream &print(std::ostream &, const Sales_data &); friend std::istream &read(std::istream &, Sales_data &); private: //Book number std::string bookNo; //sales volume unsigned units_sold = 0; //income double revenue = 0.0; };
Packaging has two important advantages:
·Ensure that user code does not inadvertently break the state of the encapsulated object.
·The specific implementation details of the encapsulated class can be changed at any time without adjusting the user level code.
Friend's statement
The declaration of a friend only specifies the access permission, not a function declaration in the usual sense. If we want the user of the class to call a friend function, we must declare the function in addition to the friend declaration. In order to make friends visible to the users of the class, we usually place the declaration of friends and the class itself in the same header file (outside the class). Therefore, our Sales_data header file should provide independent declarations for read, print and add (except for the friend declarations inside the class). Therefore, I declare these functions in the header file of Sales_data class
// Sales_ Nonmember interface of data extern Sales_data add(const Sales_data &, const Sales_data &); extern std::ostream &print(std::ostream &, const Sales_data &); extern std::istream &read(std::istream &, Sales_data &);
Hide type definitions
We can define a new type in the class. This type hides the internal implementation from the outside, and the outside does not know what the type is.
class Screen { public: typedef std::string::size_type pos; private: pos cursor = 0; pos height = 0, width = 0; std::string contents; };
Members used to define types must be defined before being used. This is different from ordinary members. The specific reasons will be explained in 7.4 Section 1 (page 254) explains. Therefore, type members usually appear at the beginning of the class.
inline member function
The so-called inline function is a means to expand at compile time and reduce runtime overhead. It can be declared through the inline keyword, or you can specify inline before defining the function in the cpp file of the class. Of course, if the member function of a class is implemented in the class header file, it is also an inline function. We improve the Screen class and implement the inline function in the above three ways
class Screen { public: typedef std::string::size_type pos; //Because Screen has another constructor //So you need to implement a default constructor Screen() = default; // cursor is initialized to 0 Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} //Read characters at cursor char get() const { //Implicit inlining return contents[cursor]; } //Show inline inline char get(pos ht, pos wd) const; //Can be made inline later Screen &move(pos r, pos c); private: pos cursor = 0; pos height = 0, width = 0; std::string contents; };
We can explicitly declare member functions with inline as part of the declaration inside the class. Similarly, we can modify the definition of functions with inline keyword outside the class: Although we do not need to specify inline at the same time in the declaration and definition, it is actually legal. However, it is best to specify inline only where it is defined outside the class, which makes the class easier to understand.
Overloaded member function
Class member functions also support overloading. As long as the function name is the same, the parameter list is different.
mutable property
If a member variable is specified with the mutable attribute, the member variable can be modified regardless of whether the object is const or whether the member function is const.
We define a mutable member variable access for Screen_ CTR, and a const member function some_member, and modify access in this function_ CTR variable.
class Screen { public: typedef std::string::size_type pos; //Because Screen has another constructor //So you need to implement a default constructor Screen() = default; // cursor is initialized to 0 Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {} //Read characters at cursor char get() const { //Implicit inlining return contents[cursor]; } //Show inline inline char get(pos ht, pos wd) const; //Can be made inline later Screen &move(pos r, pos c); void some_member() const; private: pos cursor = 0; pos height = 0, width = 0; std::string contents; //Even in a const object, access_ctr can also be modified mutable size_t access_ctr; };
Implement some_ A member variable of member
void Screen::some_member() const { //Access can also be modified in the const function_ ctr ++access_ctr; }
call chaining
When we return * this through the internal member function, that is, the class object itself, we can continue to call its internal member function in a chain. For example, we implement two set functions through overloading
Screen &Screen::set(char c) { contents[cursor] = c; return *this; } Screen &Screen::set(pos r, pos col, char ch) { //Set new value at given position contents[r * width + col] = ch; return *this; }
call chaining
Screen::pos row = 3; Screen::pos col = 4; Screen screen(3, 4, 'c'); screen.move(2, 3).set('#');
* this returned from const member is a constant pointer. If we implement a const function of display,
const Screen &Screen::display(ostream &os) const { os << "width is " << width << " " << "height is " << height << endl; return *this; }
If the compiler is called in the following chain, an error will be reported because display returns const screen & type
screen.display(cout).move(2, 3).set('#');
Therefore, we can implement chained calls through overloading to implement a display function that returns const screen & type and a display function that returns screen & type,
These two display functions call do internally_ Display function, because const function can only call const function, we first implement display function, which is a const function
void Screen::do_display(ostream &os) const { os << "width is " << width << " " << "height is " << height << endl; }
Then implement two overloaded display functions
const Screen &Screen::display(ostream &os) const { do_display(os); return *this; } Screen &Screen::display(ostream &os) { do_display(os); return *this; }
In this way, the compiler will dynamically select the version of display according to the type
Screen screen(3, 4, 'c'); screen.display(cout).move(2, 3).set('#'); const Screen cscreen(2, 1, ' '); cscreen.display(cout);
Class types and declarations
class First { int memi; int getMem(); }; struct Second { int memi; int getMem(); };
We have defined two types above, and the following assignment will report an error, because although the members in the class are the same, different classes are different types
First obj1; //Compilation error. obj1 and obj2 are not of the same type Second obj2 = obj1;
We can declare the class first without defining the class
class Bags;
This declaration is sometimes called forward declaration. It introduces the name Screen into the program and indicates that Bags is a kind type. For type Bags, it is an incomplete type before it is defined after its declaration, that is, at this time, we know that Bags is a class type, but we don't know which members it contains.
Incomplete types can only be used in very limited scenarios:
You can define a pointer or reference to this type, or you can declare (but cannot define) a function with an incomplete type as a parameter or return type.
Data members cannot be declared as this kind of type until the class is defined. We must first complete the class definition before the compiler can know how much space is required to store the data member.
Because a class is defined only after all classes are completed, the member type of a class cannot be the class itself.
However, once the name of a class appears, it is considered declared (but not yet defined), so a class is allowed to contain references or pointers to its own type:
class Link_screen { Screen window; Link_screen *next; Link_screen *prev; };
Friend classes and member functions
If A class A can be declared as A friend of another class B, class A objects can access the private members of class B objects.
class Screen { public: // Window_mgr can access the private part of the Screen class friend class Window_mgr; //Other parts of the Screen class };
Window_mgr class can access the private members of Screen class and declare window forward through class_ Mgr class.
Next, we define Window_mgr class
class Window_mgr { public: //The number of each screen in the window using ScreenIndex = std::vector<Screen>::size_type; //Resets the specified Screen to blank by number void clear(ScreenIndex); private: std::vector<Screen> screens{Screen(24, 80, ' ')}; }; void Window_mgr::clear(ScreenIndex i) { // s is a reference to Screen, pointing to the Screen we want to clear Screen &s = screens[i]; //Clear screen s.contents = string(s.height * s.width, ' '); }
You can also have member functions as friends
class Screen { public: // Window_mgr can access the private part of the Screen class friend void Window_mgr::clear(ScreenIndex); //Other parts of the Screen class };
·First define Window_mgr class, which declares the clear function, but cannot define it. Screen must be declared before clear uses the members of screen.
·Next, define the Screen, including the friend declaration for clear.
·Finally, clear is defined so that it can use the members of Screen.
Classes and nonmember functions do not have to be declared before their friend declarations. When a name first appears in a friend declaration, we implicitly assume that the name is visible in the current scope. However, the friend itself is not necessarily declared in the current scope
Source code link https://gitee.com/secondtonone1/cpplearn
My official account, thank you.