class 内部与外部定义函数的性能

Performance of defining functions inside a class vs. outside

我正在编写一个程序来玩棋盘游戏 Quoridor。我正在使用 minimax 搜索和自定义 class 来存储状态。我的问题是:定义函数 A. 在 class 内或 B. 在 class 外并传递指针是否有任何性能差异。

我的直觉(这很可能是错误的)是在情况 A 中,它会导致一些内存开销,因为 class 的每个对象都有其自己的所有函数副本。这也会减慢创建对象的速度,因为除了数据成员之外,还必须创建函数。

我的另一个问题是,对于构造函数返回的默认对象 - 使用构造函数或复制预先存在的对象更快吗?

这是 class 的代码:

class BGraph {
public:
    int rows;
    int columns;
    int walls;

    int row1;
    int column1;
    int walls1;

    int row2;
    int column2;
    int walls2;

    float time;

    // Stores whether a wall has been placed at a vertex.
    vector<int> wallsList;

    vector<set<int> > edges;

    void addEdge(int v1, int v2) {
        if (v1 > v2) {
            int t2 = v2;
            v2 = v1;
            v1 = t2;
        }
        edges[v1].insert(v2);
    }

    void removeEdge(int v1, int v2) {
        if (v1 > v2) {
            int t2 = v2;
            v2 = v1;
            v1 = t2;
        }
        edges[v1].erase(v2);
    }

    bool checkEdge(int v1, int v2) {
        if (v1 > v2) {
            int t2 = v2;
            v2 = v1;
            v1 = t2;
        }
        return edges[v1].find(v2) != edges[v1].end();
    }

    //Take row and column, return the vertex number.
    //For example, 1,1 -> 1, 2,1 -> row_length + 1
    int rc2v(int r, int c) {
        return columns * (r - 1) + c;
    }

    //Take vertex number, return (row,column)
    pair<int, int> v2rc(int v) {
        pair<int, int> ans;
        ans.first = v / columns + 1;
        ans.second = v % columns;
        if (ans.second == 0) {
            ans.second = columns;
        }
        return ans;
    }

    void addWall(Wall wall) {
        if (wall.horizontal) {
            removeEdge(rc2v(wall.row, wall.column - 1), rc2v(wall.row, wall.column));
            removeEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row - 1, wall.column));
            wallsList[rc2v(wall.row, wall.column)] = 1;
        } else {
            removeEdge(rc2v(wall.row - 1, wall.column), rc2v(wall.row, wall.column));
            removeEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row, wall.column - 1));
            wallsList[rc2v(wall.row, wall.column)] = 2;
        }
    }

    //There are two conditions - the edges that it is trying to break should EXIST.
    bool wallIsLegal(Wall wall) {
        if (wall.row <= rows && wall.row > 1 && wall.column <= columns && wall.column > 1) {
            if (wall.horizontal) {
                return checkEdge(rc2v(wall.row, wall.column - 1), rc2v(wall.row, wall.column))
                        & checkEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row - 1, wall.column))
                        & wallsList[rc2v(wall.row, wall.column)];
            } else {
                return checkEdge(rc2v(wall.row - 1, wall.column), rc2v(wall.row, wall.column))
                        & checkEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row, wall.column - 1))
                        & wallsList[rc2v(wall.row, wall.column)];
            }
        } else {
            return false;
        }
    }

    //Removes/adds edges to account for the presence of a player.
    void adjustForPlayer(int player){

        int i,j;
        if (player == 1) {
                i = row1;
                j = column1;
            } else {
                i = row2;
                j = column2;
            }

            bool e1 = (checkEdge(rc2v(i, j - 1), rc2v(i, j))); //h-x
            bool e2 = (checkEdge(rc2v(i, j), rc2v(i, j + 1))); //x-d
            bool e3 = (checkEdge(rc2v(i - 1, j), rc2v(i, j))); //b-x
            bool e4 = (checkEdge(rc2v(i, j), rc2v(i + 1, j))); //f-x

            if (e1 && e2) {
                removeEdge(rc2v(i, j - 1), rc2v(i, j));
                removeEdge(rc2v(i, j), rc2v(i, j + 1));
                addEdge(rc2v(i, j - 1), rc2v(i, j + 1));
            } else if (e1 && !e2) {
                removeEdge(rc2v(i, j - 1), rc2v(i, j));
                if (e3)
                    addEdge(rc2v(i, j - 1), rc2v(i - 1, j));
                if (e4)
                    addEdge(rc2v(i, j - 1), rc2v(i + 1, j));
            } else if (!e1 && e2) {
                removeEdge(rc2v(i, j), rc2v(i, j + 1));
                if (e3)
                    addEdge(rc2v(i - 1, j), rc2v(i, j + 1));
                if (e4)
                    addEdge(rc2v(i + 1, j), rc2v(i, j + 1));
            }

            if (e3 && e4) {
                removeEdge(rc2v(i - 1, j), rc2v(i, j));
                removeEdge(rc2v(i, j), rc2v(i + 1, j));
                addEdge(rc2v(i - 1, j), rc2v(i + 1, j));
            } else if (e3 && !e4) {
                removeEdge(rc2v(i - 1, j), rc2v(i, j));
                if (e1)
                    addEdge(rc2v(i - 1, j), rc2v(i, j - 1));
                if (e2)
                    addEdge(rc2v(i - 1, j), rc2v(i, j + 1));
            } else if (!e3 && e4) {
                removeEdge(rc2v(i, j), rc2v(i + 1, j));
                if (e1)
                    addEdge(rc2v(i + 1, j), rc2v(i, j - 1));
                if (e2)
                    addEdge(rc2v(i + 1, j), rc2v(i, j + 1));
            }
    }

    BGraph(int r, int c, int k, int t) {
        rows = r;
        columns = c;
        walls = k;
        time = t;

        row1 = r;
        column1 = c / 2 + 1;
        walls1 = k;
        row2 = 1;
        column2 = c / 2 + 1;
        walls2 = k;

        //r*c+1 so that we don't have to worry about the fact that these things are actually indexed from zero.
        wallsList = vector<int>(r*c+1, 0);

        for (int i = 1; i <= r * c + 1; i++) {
            set<int> s;
        }

        //Initialise horizontal edges.
        for (int i = 1; i <= r; i++) {
            for (int j = 1; j < c; j++) {
                addEdge(rc2v(i, j), rc2v(i, j + 1));
            }
        }

        //Initialise vertical edges.
        for (int j = 1; j <= c; j++) {
            for (int i = 1; i < r; i++) {
                addEdge(rc2v(i, j), rc2v(i + 1, j));
            }
        }
    }

    //Copy Constructor
    BGraph(const BGraph & obj){
        rows = obj.rows;
        columns = obj.columns;
        walls = obj.walls;

        row1 = obj.row1;
        column1 = obj.column1;
        walls1 = obj.walls1;

        row2 = obj.row2;
        column2 = obj.column2;
        walls2 = obj.walls2;

        time = obj.time;

        wallsList = obj.wallsList;
        edges = obj.edges;
    }

};

关于你的两个问题:

1) 在 class 内部/外部定义函数是否有任何性能差异?

如果将函数定义为非虚拟函数,那么在性能方面与在 class 之外定义函数没有任何区别。

每个成员非静态函数都有一个隐式参数 this ,这很容易导致一个函数的原型,该函数的指针作为在 class.

之外定义的参数

示例代码:

  class A {
    void foo(){};
  }

  void outsideFoo(A* ptr);

  // A::foo() true signature is A::foo(A* this) 
  // which is identical to outsideFoo(A* ptr)

非虚函数在编译时解析,因此您将拥有相同数量的间接寻址。

但是,如果要将函数设为虚拟,则它们将比在 class 之外定义函数慢,因为在运行时将在虚拟函数 table 内部进行搜索,然后跳转到函数 .

对于你的情况,我建议你在 class 中使用非虚拟函数,因为它会使代码清晰。

2) 构造函数与复制构造函数

在你的情况下,它们都一样快,因为你正在制作深拷贝。

调用构造函数与复制构造函数的性能差异由实现定义。例如,如果您要对数据进行一些改进,例如生成数据的空间划分或数据排序,那么复制构造函数会更快,因为它只会复制已经生成的空间划分/排序数据。

但是,如果您直接在两者中分配/赋值,性能将是相同的。