Передача C++ ссылкой

Я недавно имею (4 дня) начал изучать C++, прибывающий из C / фон Java. Для изучения нового языка я, ussualy запускаются путем перереализации различных классических алгоритмов как язык, конкретный, как я могу.

Я пришел к этому коду, это - DFS - Поиск в глубину в неориентированном графике. Все еще от то, что я считал, лучше передавать параметры ссылками в C++. К сожалению, я не могу вполне схватить понятие ссылки. Каждый раз, когда мне нужна ссылка, я запутываюсь, и я думаю с точки зрения указателей. В моем текущем коде я использую передачу значением.

Вот код (вероятно, не 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++?

Существует ли стандартная альтернатива для вектора и типа ** для bi размерных массивов в C++?

БОЛЕЕ ПОЗДНЕЕ РЕДАКТИРОВАНИЕ:

Хорошо, я проанализировал Ваши ответы (благодарит все), и я переписал код большим способом ООП. Также я имею, понимают то, что ссылка и должны были использовать его. Это несколько подобно указателю константы, кроме того, что указатель того типа может содержать ПУСТОЙ УКАЗАТЕЛЬ.

Это - мой последний код:

#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);
}
5
задан dsolimano 24 October 2011 в 18:33
поделиться

4 ответа

За 4 дня освоения C ++ вы отлично справляетесь. Вы уже используете стандартные контейнеры, алгоритмы и пишете собственные шаблоны функций. Самая большая проблема, которую я вижу, - это именно ссылка на ваш вопрос: необходимость передачи по ссылке / константной ссылке.

Каждый раз, когда вы передаете / возвращаете объект C ++ по значению, вы вызываете глубокую копию его содержимого. Это совсем не дешево, особенно для чего-то вроде вашего матричного класса.

Сначала давайте посмотрим на showMatrix.Цель этой функции - вывести содержимое матрицы. Нужна ли копия? Нет. Нужно ли что-то менять в матрице? Нет, это просто показать это. Таким образом, мы хотим передать матрицу по константной ссылке.

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

[Примечание: я использовал несколько определений типов, чтобы упростить чтение и запись. Я рекомендую его, когда у вас много параметризации шаблонов].

Теперь давайте посмотрим на getMatrixFromFile:

SquareMatrix getMatrixFromFile(string fName);

Возврат SquareMatrix по значению здесь может быть дорогостоящим (в зависимости от того, применяет ли ваш компилятор оптимизацию возвращаемого значения к этому случаю), и поэтому он передает строку по значению. С C ++ 0x у нас есть ссылки rvalue, чтобы сделать это, поэтому нам не нужно возвращать копию (я также изменил строку, которая будет передана по ссылке const по тем же причинам, что и showMatrix, нам не нужна копия имя файла):

SquareMatrix&& getMatrixFromFile(const string& fName);

Однако, если у вас нет компилятора с этими функциями, то общий компромисс - передать матрицу по ссылке и позволить функции заполнить ее:

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

Это не дает обеспечить максимально удобный синтаксис для клиента (теперь они должны писать две строки кода вместо одной), но при этом последовательно избегает накладных расходов на глубокое копирование. Для решения этой проблемы существует также MOJO , но он станет устаревшим с C ++ 0x.

Простое практическое правило: если у вас есть какой-либо определяемый пользователем тип (не простой старый тип данных) и вы хотите передать его функции:

  1. переходите по константной ссылке, если функции нужно только читать из Это.
  2. передать по ссылке, если функции нужно изменить оригинал.
  3. передается по значению , только если функции требуется копия для изменения.

Существуют исключения, когда у вас может быть дешевый UDT (определяемый пользователем тип), который дешевле копировать, чем передавать, например, по константной ссылке, но пока придерживайтесь этого правила, и вы будете в пути. писать безопасный и эффективный код на C ++, который не тратит драгоценные тактовые циклы на ненужные копии (обычное наказание для плохо написанных программ на C ++).

8
ответ дан 18 December 2019 в 16:35
поделиться

Ссылки и указатели тесно связаны между собой. Оба являются способами передачи параметров без копирования значения параметра в стековый фрейм подпрограммы.

Основное различие между ними:

  • Указатель p указывает на объект o.
  • Ссылка i является объектом o. Другими словами, в псевдониме.

Чтобы еще больше запутать ситуацию, насколько я знаю, реализация компилятора между этими двумя вариантами практически одинакова.

Представьте себе функции Ptr(const T* t) и Ref(const T& t).

int main() { int a; Ptr(&a); Ref(a); }

В Ptr, t будет указывать на местоположение a. Вы можете разыменовать его и получить значение a. Если вы сделаете &t (возьмете адрес t), то получите адрес параметра.

In Ref, t is a. Вы можете использовать a для значения a. Вы можете получить адрес a с помощью &a. Это небольшой синтаксический сахар, который дает вам С++.

Оба языка предоставляют механизм для передачи параметров без копирования. В вашей функции (кстати, декларация не нужна):

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

Каждый раз, когда она вызывается, T будет копироваться. Если T - большой вектор, то копируются все данные в векторе. Это довольно неэффективно. Вы не хотите передавать весь вектор в новый кадр стека, вы хотите сказать: "Эй - новый кадр стека, используйте данные this". Поэтому вы можете передавать по ссылке. Как это выглядит?

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

elem - это const, потому что он не изменяется функцией. Она также будет использовать память для elem, которая хранится в вызывающей функции, а не копировать ее в стек.

Опять же, по той же причине (чтобы избежать копирования параметров), используйте:

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 и продолжаете ссылаться на f. Стековый кадр, создавший f, исчез, и это местоположение больше не действительно, и вы ссылаетесь на ненужную память. В этом случае вам придется вернуться по значению (или выделить новый указатель на куче).

Единственное исключение из правила "не возвращать ссылки" - это когда вы знаете, что память переживет стек. Именно так STL реализует operator[] для своих контейнеров.

Надеюсь, это поможет! :)

1
ответ дан 18 December 2019 в 16:35
поделиться

Для передачи по ссылке вы обычно меняете это:

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 или Фортран. Вы можете перегрузить оператор [] таким образом, чтобы вы могли использовать эту нотацию, если хотите, но это изрядный объем дополнительной работы с (IMO) небольшой реальной пользой.

2
ответ дан 18 December 2019 в 16:35
поделиться
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);

Некоторые из них я смог понять. И по возможности, если вы не меняете или не собираетесь менять состояние объекта внутри тела вашего метода, сделайте передаваемые переменные const.

Я бы не просил вас включать все конструкции C++ в первую попытку, но постепенно, чтобы вы не довели себя до депрессии. Vector является наиболее используемым контейнером STL. И использование контейнеров зависит от ваших потребностей, а не от прихоти использовать один контейнер вместо другого.

Одно краткое описание контейнеров. http://msdn.microsoft.com/en-us/library/1fe2x6kt%28VS.80%29.aspx

@Jerry Спасибо за правку. Вектор не является чрезмерно используемым, но используется больше из-за своей простоты для простых объектов, а не больших монолитных объектов класса. Он похож на массив в стиле C, но не является им, с большим количеством дополнительных алгоритмов. Еще два, которые используются довольно часто, - это карты и списки. Возможно, это так, потому что в местах, где я работаю, использование этих контейнеров требуется чаще, чем в других местах.

1
ответ дан 18 December 2019 в 16:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: