Statement on the definition, forward statements, include, circular dependencies, ordinary friend functions, friend classes, member functions are summarized friend

Do "C ++ Primer" (5th edition) 253 7.3.4 felt exercises, it summarizes the

1 statement

1.1 declare variables and functions

Common declaration is to declare a variable or function, usually in the header file .hdeclared, for example:

pos cursor = 0;    // 给定初始值

char get(pos r, pos col) const;

1.2 declare the class

For a class, usually directly written directly in the header file class ClassName { ... }, which is called class definitions , and then in the class body {...}and is declared or defined member variables and member functions. The class declaration is no class body, only a class name:

class Screen;    // 声明一个类

Such a class declaration is incomplete type, it can not create an object or access its members.

2 Definitions

The function is defined for the function definition portion, or a body portion defining class, or a variable. Function or variable general declaration in the header file, then the corresponding function defined in the source file.

For a classclass , are generally defined in the header file, is a member of a class or a function variable declarations, and then in the source file #include xxx.h, and then define the class member functions.

In. cppFile #include .hafter file, this time the class has been defined, so you can access its members.

3 forward declaration

Generally forward declaration function or class, the class forward declaration is generally used in the header file, such as a class definition, it requires a certain type of class, its members can not access or create objects, but this time the class has not yet been definition, not includebefore it, then you can write a statement to the first use, after includethe first document of this place ( .hor .cpp) the definition of the forward declaration. Forward declarations of functions are normally used in the source file (if the function before the declaration header file is not called to declare it, it makes no difference to the declarations and statements actually before).

4 include cyclic dependency and

include the class definition or function declaration into the current file, but if the two classes in its definition of headers each other in include, and then use the class type which, at this time, when the class definition of class A B not defined, and vice versa. Depend on cycles error has occurred. Therefore, includeonly one-way quote, backwards like a tree, from the roots to the trunk, following includethe above, can not go visit something that does not exist on logic.

Resolve the circular dependency issue, direct way is to sort out the parent-child relationship calls, such as providing references Bh A, then B is a forward declaration by using B type in Ah. Of course, this can only be used only type B, and B can not access members or create objects.

In the two classes of .cppdocuments to each other includeit is legitimate, because the two classes which are defined in the header file is completed.

5 common friend function

Note: The friend function is just a function of the authority declared, is not equal to the function declaration.

Declaration of a general friend functions only need to add a friendfunction declaration keyword in the class can be relatively simple and defined in the source file.

For example: Sales_data.h

#pragma once
#include <string>
#include <ostream>
#include <istream>

class Sales_data
{
// 为Sales_data的非成员函数所作的友元声明
friend Sales_data add(const Sales_data&, const Sales_data&);
friend std::ostream &print(std::ostream&, const Sales_data&);
friend std::istream &read(std::istream&, Sales_data&); 
// 其他成员及访问说明符与之前一致
public:
    // 构造函数
    // 空参的默认构造函数,居然还有这种操作,C++11标准的东西,表示默认行为
    Sales_data() = default;

    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) { } 

    // 不同于上面三个类内定义的构造函数,这个将在类外(cpp文件)定义
    Sales_data(std::istream &is);

    ~Sales_data() = default;  // 析构函数

    // 成员函数: 关于Sales_data对象的操作
    // 注意: 定义在类内部的函数是隐式的inline函数,这里的const与this指针有关,在下面另开篇幅单独说明
    std::string isbin() const { return bookNo; }        // 返回对象的ISBN编号
    Sales_data& combine(const Sales_data &rhs);         // 将一个Sales_data对象加到另一个对象上

private:
    // 数据变量: 定义成员的属性
    std::string bookNo;                                 // ISBN编号
    unsigned units_sold = 0;                            // 销量
    double revenue = 0.0;                               // 总销售收入
    double avg_price() const;                           // 返回出售书籍的平均价格
};

// Sales_data的非成员接口函数,上面已声明为类的友元,可以访问私有成员
Sales_data add(const Sales_data&, const Sales_data&);   // 执行两个Sales_data对象的加法,后期以重载运算符代替
std::ostream &print(std::ostream&, const Sales_data&);  // 将Sales_data对象的值输出到ostream
std::istream &read(std::istream&, Sales_data&);         // 将数据从istream读入到Sales_data中

7 Tomomoto类

Friend class declaration is also relatively simple, only need to add the friendtype declaration keyword in the main class, the first here to use friend class declaration:

For example, there is a class: Window_msg, Window_msg follows

#pragma once
#include <vector>
#include "Screen.h"

/**
 * 窗口管理类
 * 表示显示器上的一组Screen
 */
class Window_mgr
{
public:
    Window_mgr();
    ~Window_mgr();

    // 窗口中每个屏幕的编号类型
    using ScreenIndex = std::vector<Screen>::size_type;
    
    // 按照编号将指定的Screen重置为空白
    void clear(ScreenIndex sindex);
private:
    // 这个Window_mgr追踪的Screen
    // 类内初始值:默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
    std::vector<Screen> screens{Screen(25, 25, ' ')};
    // 如上所示,当我们提供一个类内初始值时,必须以 "=" 或 "{ }" 表示的直接初始化
};

We will declare it as another class: Screen's friend, Screen.h file as follows:

#pragma once
#include <string>
#include <ostream>

// 前向声明,防止循环引用,Window_mgr.h单向引用了Screen.h,所以符合先声明再定义的原则
class Window_mgr;
/**
 * 窗口类
 */
class Screen
{
public:
    // 声明Window_mgr类为Screen类的友元,友元类的成员函数可以访问此类的所有成员
    // 友元可以是某个类,某个类的成员函数,或普通函数
    friend class Window_mgr;     // 此时只用到Window_mgr类型,而不用其成员,所以可以使用前向声明

    // 声明一个类型别名,或可使用typedef,用户可以使用这个名字
    using pos = std::string::size_type;

    Screen() = default;     // 默认构造函数,内联
    ~Screen() = default;    // 默认析构函数,内联

    // 自定义构造函数
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(width * height, c){ }

    // 读取光标处的字符,隐式内联
    char get() const { return contents[cursor]; }

    // 重载,类成员函数要想声明内联,必须在声明的同时定义,就像上一个get一样,否则无法编译
    char get(pos r, pos col) const;

    // 设置光标所在位置的字符
    Screen &set(char c);

    // 设置给定位置的字符
    Screen &set(pos r, pos col, char c);

    // 移动光标
    Screen &move(pos r, pos c);

    // 打印屏幕内容,返回当前对象的常量引用
    Screen &display(std::ostream &os);

    // 根据调用对象是否是const重载display函数
    const Screen &display(std::ostream &os) const;

private:
    pos cursor = 0;                 // 鼠标位置
    pos height = 0, width = 0;      // 屏幕宽高
    std::string contents;           // 屏幕内容

    // 可变数据成员,即使在一个const对象内也能被修改,用来对函数调用计数
    mutable size_t access_ctr;

    void do_display(std::ostream &os) const;
};

After declaring the friend class, all the functions of a friend class can access all members of the master class, obviously not safe.

8 friend member function

Two classes as shown above, only the clear statement Window_msg class member function as a friend function Screen, rather than the entire class, which would be a lot of trouble.

To access this point in Screen.h members Window_msg in clear, apparently can not be used before the declaration, must #include Window_msg.h, therefore:

  • First define Window_msg class, which states clear function, but can not define it. Because the Screen must be defined before the clear use of members of the Screen
  • Next, define the Screen, including the friend declaration to clear the
  • Finally, the definition of clear, at which point it can use a member of the Screen

Above foreshadowing many declarations, definitions, a forward declaration is to understand these words, implement the following:

Window_mgr.h

#pragma once
#include <vector>

// 该类会被Screen类引用,所以这里可以使用前向声明
// 前向声明只能使用类型名,不能访问成员
class Screen;

/**
 * 窗口管理类
 * 表示显示器上的一组Screen
 */
class Window_mgr
{
public:
    Window_mgr();
    ~Window_mgr();

    // 窗口中每个屏幕的编号类型
    using ScreenIndex = std::vector<Screen>::size_type;
    
    // 按照编号将指定的Screen重置为空白
    void clear(ScreenIndex sindex);
private:
    // 这个Window_mgr追踪的Screen
    // 类内初始值:默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
    std::vector<Screen> screens;    // 前向声明无法访问成员,所以这里不能赋予默认值
    // 如上所示,当我们提供一个类内初始值时,必须以 "=" 或 "{ }" 表示的直接初始化
};

Screen.h

#pragma once
#include <string>
#include <ostream>
#include "Window_mgr.h"

/**
 * 窗口类
 */
class Screen
{
public:
    // 声明Window_mgr类为Screen类的友元,友元类的成员函数可以访问此类的所有成员
    // 友元可以是某个类,某个类的成员函数,或普通函数

    // 声明友元成员函数:将Window_mgr类的clear成员声明为Screen的友元
    // 要访问Window_mgr的成员就必须先定义它,然后include到这里,不能是前向声明
    friend void Window_mgr::clear(ScreenIndex si);

    // 声明一个类型别名,或可使用typedef,用户可以使用这个名字
    using pos = std::string::size_type;

    Screen() = default;     // 默认构造函数,内联
    ~Screen() = default;    // 默认析构函数,内联

    // 自定义构造函数
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(width * height, c){ }

    // 读取光标处的字符,隐式内联
    char get() const { return contents[cursor]; }

    // 重载,类成员函数要想声明内联,必须在声明的同时定义,就像上一个get一样,否则无法编译
    char get(pos r, pos col) const;

    // 设置光标所在位置的字符
    Screen &set(char c);

    // 设置给定位置的字符
    Screen &set(pos r, pos col, char c);

    // 移动光标
    Screen &move(pos r, pos c);

    // 打印屏幕内容,返回当前对象的常量引用
    Screen &display(std::ostream &os);

    // 根据调用对象是否是const重载display函数
    const Screen &display(std::ostream &os) const;

private:
    pos cursor = 0;                 // 鼠标位置
    pos height = 0, width = 0;      // 屏幕宽高
    std::string contents;           // 屏幕内容

    // 可变数据成员,即使在一个const对象内也能被修改,用来对函数调用计数
    mutable size_t access_ctr;

    void do_display(std::ostream &os) const;
};

Window_mgr.cpp

#include "Window_mgr.h"
#include "Screen.h"     // 定义中需要访问Screen的成员,所以此时要include,不能再使用前向声明

Window_mgr::Window_mgr(/* args */)
{
}

Window_mgr::~Window_mgr()
{
}

void Window_mgr::clear(ScreenIndex sindex)
{
    // s是一个Screen类型的引用,指向我们想清空的那个屏幕
    Screen &s = screens[sindex];
    // 将选定的Screen重置为空白
    s.contents = std::string(s.height * s.width, ' ');
}

Focus: Use the friend category Window_msg header file before the main class to a statement in the source file using the includemain class, the definition of member functions and access the main class members to avoid circular dependencies. Directly in the main class header file includefriend classes.

Guess you like

Origin www.cnblogs.com/jixiaohua/p/12578719.html