代码之家  ›  专栏  ›  技术社区  ›  Andrei Ciobanu

C++传递引用

  •  5
  • Andrei Ciobanu  · 技术社区  · 14 年前

    我最近(4天)开始学习C/C++ java背景下的C++。为了学习一门新的语言,我通常从重新实现不同的经典算法开始,尽可能地针对特定的语言。

    我来看看这段代码,它是在无方向图中的DFS深度优先搜索。不过,从我读到的,最好通过C++中的引用传递参数。不幸的是,我不能完全理解引用的概念。每次我需要一个参考,我都会感到困惑,我会用指针来思考。在我当前的代码中,我使用passby值。

    下面是代码(可能不是Cppthonic):

    #include <algorithm>
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <stack>
    #include <vector>
    
    using namespace std;
    
    template <class T>
    void utilShow(T elem);
    
    template <class T>
    void utilShow(T elem){
        cout << elem << " ";
    }
    
    vector< vector<short> > getMatrixFromFile(string fName);
    void showMatrix(vector< vector<short> > mat);
    vector<unsigned int> DFS(vector< vector<short> > mat);
    
    /* Reads matrix from file (fName) */
    vector< vector<short> > getMatrixFromFile(string fName)
    {
        unsigned int mDim;
        ifstream in(fName.c_str());
        in >> mDim;
        vector< vector<short> > mat(mDim, vector<short>(mDim));
        for(int i = 0; i < mDim; ++i) {
            for(int j = 0; j < mDim; ++j) {
                in >> mat[i][j];
            }
        }
        return mat;
    }
    
    /* Output matrix to stdout */
    void showMatrix(vector< vector<short> > mat){
        vector< vector<short> >::iterator row;
        for(row = mat.begin(); row < mat.end(); ++row){
            for_each((*row).begin(), (*row).end(), utilShow<short>);
            cout << endl;
        }
    }
    
    /* DFS */
    vector<unsigned int> DFS(vector< vector<short> > mat){
        // Gives the order for DFS when visiting
        stack<unsigned int> nodeStack;
        // Tracks the visited nodes
        vector<bool> visited(mat.size(), false);
        vector<unsigned int> result;
        nodeStack.push(0);
        visited[0] = true;
        while(!nodeStack.empty()) {
            unsigned int cIdx = nodeStack.top();
            nodeStack.pop();
            result.push_back(cIdx);
            for(int i = 0; i < mat.size(); ++i) {
                if(1 == mat[cIdx][i] && !visited[i]) {
                    nodeStack.push(i);
                    visited[i] = true;
                }
            }
        }
        return result;
    }
    
    int main()
    {
        vector< vector<short> > mat;
        mat = getMatrixFromFile("Ex04.in");
        vector<unsigned int> dfsResult = DFS(mat);
    
        cout << "Adjancency Matrix: " << endl;
        showMatrix(mat);
    
        cout << endl << "DFS: " << endl;
        for_each(dfsResult.begin(), dfsResult.end(), utilShow<unsigned int>);
    
        return (0);
    }
    

    你能给我一些关于如何使用引用的提示吗,通过引用这个代码?

    对于C++中的二维数组,是否存在向量和类型**的标准替代方案?

    以后编辑:

    好的,我已经分析了你的答案(谢谢大家),并且我已经用更面向对象的方式重写了代码。同时我也明白了什么是参考资料,我们应该使用它。它有点类似于const指针,只是这种类型的指针可以包含NULL。

    这是我的最新代码:

    #include <algorithm>
    #include <fstream>
    #include <iostream>
    #include <ostream>
    #include <stack>
    #include <string>
    #include <vector>
    
    using namespace std;
    
    template <class T> void showUtil(T elem);
    
    /**
    * Wrapper around a graph
    **/
    template <class T>
    class SGraph
    {
    private:
        size_t nodes;
        vector<T> pmatrix;
    public:
        SGraph(): nodes(0), pmatrix(0) { }
        SGraph(size_t nodes): nodes(nodes), pmatrix(nodes * nodes) { }
        // Initialize graph from file name
        SGraph(string &file_name);
        void resize(size_t new_size);
        void print();
        void DFS(vector<size_t> &results, size_t start_node);
        // Used to retrieve indexes.
        T & operator()(size_t row, size_t col) {
            return pmatrix[row * nodes + col];
        }
    };
    
    template <class T>
    SGraph<T>::SGraph(string &file_name)
    {
        ifstream in(file_name.c_str());
        in >> nodes;
        pmatrix = vector<T>(nodes * nodes);
        for(int i = 0; i < nodes; ++i) {
            for(int j = 0; j < nodes; ++j) {
                in >> pmatrix[i*nodes+j];
            }
        }
    }
    
    template <class T>
    void SGraph<T>::resize(size_t new_size)
    {
        this->pmatrix.resize(new_size * new_size);
    }
    
    template <class T>
    void SGraph<T>::print()
    {
        for(int i = 0; i < nodes; ++i){
            cout << pmatrix[i];
            if(i % nodes == 0){
                cout << endl;
            }
        }
    }
    
    template <class T>
    void SGraph<T>::DFS(vector<size_t> &results, size_t start_node)
    {
        stack<size_t> nodeStack;
        vector<bool> visited(nodes * nodes, 0);
        nodeStack.push(start_node);
        visited[start_node] = true;
        while(!nodeStack.empty()){
            size_t cIdx = nodeStack.top();
            nodeStack.pop();
            results.push_back(cIdx);
            for(int i = 0; i < nodes; ++i){
                if(pmatrix[nodes*cIdx + i] && !visited[i]){
                    nodeStack.push(i);
                    visited[i] = 1;
                }
            }
        }
    }
    
    template <class T>
    void showUtil(T elem){
        cout << elem << " ";
    }
    
    int main(int argc, char *argv[])
    {
        string file_name = "Ex04.in";
        vector<size_t> dfs_results;
    
        SGraph<short> g(file_name);
        g.DFS(dfs_results, 0);
    
        for_each(dfs_results.begin(), dfs_results.end(), showUtil<size_t>);
    
        return (0);
    }
    
    4 回复  |  直到 13 年前
        1
  •  8
  •   stinky472    14 年前

    首先让我们看看showMatrix。此函数的目的是输出矩阵的内容。需要复印件吗?不,它需要改变矩阵中的任何东西吗?不,它的目的只是展示它。因此,我们希望通过常量引用传递矩阵。

    typedef vector<short> Row;
    typedef vector<Row> SquareMatrix;
    void showMatrix(const SquareMatrix& mat);
    

    现在让我们看看getMatrixFromFile:

    SquareMatrix getMatrixFromFile(string fName);
    

    在这里按值返回SquareMatrix可能代价高昂(这取决于编译器是否对这种情况应用了返回值优化),按值传入字符串也是如此。使用C++ 0x,我们有RValk引用来实现它,因此我们不必返回一个副本(我也通过与SkMatkMatt相同的原因修改了const引用传递的字符串,我们不需要文件名的副本):

    SquareMatrix&& getMatrixFromFile(const string& fName);
    

    但是,如果您没有具有这些功能的编译器,那么一种常见的折衷方法是通过引用传入矩阵,并让函数填充它:

    void getMatrixFromFile(const string& fName, SquareMatrix& out_matrix);
    

    这并没有为客户机提供方便的语法(现在他们必须编写两行代码而不是一行代码),但它始终避免了深度复制开销。还有 MOJO

    1. 如果函数需要修改原始函数,则通过引用传递。
    2. 传递值 只有 函数需要一个副本来修改。

        2
  •  2
  •   Jerry Coffin    14 年前

    vector<unsigned int> DFS(vector< vector<short> > mat){
    

    收件人:

    vector<unsigned int> DFS(vector<vector<short>> const &mat) { 
    

    从技术上讲,这是通过 const 引用,但当/如果您不打算修改原始对象时,通常希望使用它。

    另一方面,我可能会改变这一点:

    for_each((*row).begin(), (*row).end(), utilShow<short>);
    

    例如:

    std::copy(row->begin(), row->end(), std::ostream_iterator<short>(std::cout, " "));
    

    同样地:

    for_each(dfsResult.begin(), dfsResult.end(), utilShow<unsigned int>);
    

    将变成:

    std::copy(dfsResult.begin(), dfsResult.end(),
              std::ostream_iterator<unsigned int>(std::cout, " "));
    

    (…这看起来可以避免 utilShow

    就2D矩阵而言,除非需要不规则矩阵(不同的行可以有不同的长度),否则通常使用简单的前端来处理单个向量中的索引:

    template <class T>
    class matrix { 
        std::vector<T> data_;
        size_t columns_;
    public:
        matrix(size_t rows, size_t columns) : columns_(columns), data_(rows * columns)  {}
    
        T &operator()(size_t row, size_t column) { return data[row * columns_ + column]; }
    };
    

    请注意,这使用 operator() 为了索引,所以 m[x][y] m(x,y) ,类似于BASIC或Fortran。你可以超载 operator[] 在某种程度上,如果您愿意的话,您可以使用这种表示法,但这是一个相当多的额外工作(IMO)几乎没有实际好处。

        3
  •  1
  •   Stephen    14 年前

    引用和指针是密切相关的。这两种方法都是传递参数,而不将参数值复制到子例程的堆栈帧上。

    • 指针 p 指向一个对象 o .
    • 参考文献 i o . 换句话说,在别名中。

    更让人困惑的是,据我所知,两者之间的编译器实现基本相同。

    想象一下这个功能 Ptr(const T* t) Ref(const T& t)

    int main(){ 内景a; 参考(a); }

    Ptr , t 会指向 a . 您可以取消对它的引用并获得 &t (请填写 t型

    Ref , . 你可以用 为了价值 具有 &a . 这是c++给你的一点语法甜点。

    两者都提供了一种无需复制即可传递参数的机制。在您的函数中(顺便说一下,您不需要声明):

    template <class T> void utilShow(T elem) { ... }
    

    T 将被复制。如果T是一个大的向量,它将复制向量中的所有数据。这是相当低效的。你不想把整个向量传递给新的堆栈帧,你想说“嘿-新堆栈帧,使用 “数据”。所以你可以通过引用。那是什么样子的?

    template <class T> void utilShow(const T &elem) { ... }
    

    elem const ,因为它不会被函数改变。它还将使用内存 存储在调用者中,而不是将其复制到堆栈中。

    同样,出于同样的原因(为了避免参数的副本),请使用:

    vector< vector<short> > getMatrixFromFile(const string &fName) { ... }
    void showMatrix(const vector< vector<short> > &mat) { ... }
    

    一个棘手的部分是,你可能会想:“嘿,一个参考意味着没有副本!我要一直用它!我要返回函数的引用!”这就是你的程序崩溃的地方。

    // Don't do this!
    Foo& BrokenReturnRef() {
      Foo f;
      return f;
    }
    
    int main() {
      Foo &f = BrokenReturnRef();
      cout << f.bar();
    }
    

    不幸的是,这个坏了!什么时候? BrokenReturnRef 跑, f main 继续引用 . 创建的堆栈帧 已经消失,并且该位置不再有效,并且您正在引用垃圾内存。在这种情况下,你会 按值返回(或在堆上分配一个新指针)。

    operator[] 为了它的容器。

        4
  •  1
  •   DumbCoder    14 年前
    void utilShow(T& elem);
    vector< vector<short> > getMatrixFromFile(const string& fName);
    void showMatrix(vector< vector<short> >& mat);
    vector<unsigned int> DFS(vector< vector<short> >& mat);
    

    我不会要求你在第一次尝试中包括所有C++结构,但逐渐地不要让自己陷入抑郁状态。Vector是最常用的STL容器。容器的使用取决于你的需要,而不是一个一个地使用。

    http://msdn.microsoft.com/en-us/library/1fe2x6kt%28VS.80%29.aspx

    @谢谢你的编辑。