Беспорядок в C++

Я очень плохо знаком с C++, и я в настоящее время изучаю это. Я получил несколько вопросов..

  1. Каковы различия между пустым DoSomething (константа нечто Foo&) и пустым DoSomething (Нечто Foo)? Если мы не указываем, и затем экземпляр Foo будет передан значением (не ссылка). Это совпадет с константой наличия + и в аргументе ни кроме какой проверки во время компиляции. Так, Почему делает константу наличия + и становятся лучшей практикой по аргументу без и и константа?

    В C#, передавая объект "ссылкой", но кажется, что это не находится в C++.

  2. В книге, которую я читаю, было сказано, что Функции членства передают неявный параметр ссылкой..

    Кто-либо мог дать мне образец неявного параметра и ссылкой? Я знаю, что, если мы хотим передать объект ссылкой, мы должны использовать и (например, Foo (Person& p)), но каким образом передача C++ объект ссылкой для неявного параметра? Я считал, что неявный параметр в C++ похож на Contructor (строковая ул.): strMemberVariable (ул.) {}...

  3. Действительно ли массив является единственными, которые передают ссылкой в C++?

  4. Почему я не могу использовать Foo fInstance в классе Foo?

Пример:

class Foo {

public:    
    Foo() { }

    Foo(const Foo& f) : fInstance(f) {   }  

    Foo fInstance;      
};

Заранее спасибо.

5
задан abatishchev 27 June 2010 в 12:12
поделиться

12 ответов

1 В чем разница между void DoSomething (const Foo & foo) и void DoSomething (Foo foo) ? Если мы не укажем &, то экземпляр Foo будет передан по значению (а не по ссылке). Это будет то же самое, что и аргумент const + & in, за исключением отсутствия проверки во время компиляции. Итак, почему использование const + & становится лучшей практикой по сравнению с аргументом без & и const?

В C # передача объекта осуществляется «по ссылке», но, похоже, этого нет в C ++.

Есть несколько различий в порядке важности:

  • Если объект Foo не может быть скопирован, вам необходимо передать его по ссылке
  • Если объект Foo является базовым классом, вы должны получить его по ссылке, чтобы пользователи могли вызывать ваши функции с производными классами
  • Значение фактического объекта может измениться, даже если у вас есть ссылка на него const
  • Эффективность, копирование типов пользователей может быть дорогостоящим, но компиляторы могут быть достаточно умными, чтобы понять это, так что ...

2 В книге, которую я читаю, сказано, что функции-члены передают неявный параметр по ссылке.

Может ли кто-нибудь дать мне образец неявного параметра и по ссылке? Я знаю, что если мы хотим передать объект по ссылке, нам нужно использовать & (например, Foo (Person & p)), но почему С ++ передает объект по ссылке для неявного параметра? Я читал, что неявный параметр в C ++ похож на Contructor (string str): strMemberVariable (str) {} ...

Под неявным параметром вы должны понимать this , то есть сам объект. Он эффективно передается по ссылке, поскольку вы можете изменить его состояние в функции-члене.

Следуя замечанию Конрада : обратите внимание, что this сам по себе не передается по ссылке, this является ссылкой (указателем) на объект, но является передается по значению. Вы не можете изменить адрес памяти вашего объекта по своему усмотрению;)

3 Является ли массив единственным, который передается по ссылке в C ++?

Нет. Вы увидите изменения в элементах массива, но массив (структура) не изменится.

После замечания FredOverflow , иллюстрация:

void fun(int* p, size_t size);

int main(int argc, char* argv[])
{
  int array[15];
  fun(array, 15);
}

Мы не знаем, что делает fun , он, вероятно, изменит некоторые элементы массива , но каким бы ни было его действие, array останется массивом из 15 целых чисел: содержимое изменяется, а структура - нет.

В результате, чтобы изменить массив , нам нужно другое объявление:

void changer(int*& array, size_t& size);

Таким образом, мы можем изменить как содержимое, так и структуру (а также передать новый размер).И, конечно же, мы можем вызывать эту функцию только с массивом, который был размещен динамически.

4 Почему я не могу использовать Foo fInstance в классе Foo?

Потому что это бесконечная рекурсия. Подумайте об этом с точки зрения компилятора и попробуйте угадать размер Foo . Размер Foo - это сумма размеров его атрибутов, плюс, возможно, некоторая информация о заполнении и типе. Кроме того, размер объекта составляет не менее 1 , так что к нему можно обращаться. Итак, если Foo имеет Foo , каков его размер :)?

Обычное решение - использовать интеллектуальный указатель:

class Foo
{
public:

private:
  std::unique_ptr<Foo> mInstance;
};

Поскольку размер указателя не зависят от размера указанного объекта, поэтому здесь не происходит рекурсии :)

10
ответ дан 18 December 2019 в 07:28
поделиться

Поскольку здесь так много заблуждений и откровенно ложных ответов, это моя попытка исправить ситуацию:

В чем разница между void DoSomething(const Foo& foo) и void DoSomething(Foo foo)?

Как уже говорили другие, второй код требует копирования (обычно вызывается конструктор копирования Foo).

Итак, почему наличие const + & становится лучшей практикой по сравнению с аргументом без & и const?

Есть несколько специальных целей, на которые другие уже ответили (например, полиморфизм во время выполнения). Это не объясняет, почему это стало лучшей практикой. Причина проста до безобразия: потому что это в разы эффективнее. Представьте себе передачу вектора или строки другому методу - или, по сути, любой большой структуры данных. Стоимость копирования будет, как правило, огромной, а методы могут часто вызываться в коде - на самом деле, методы обычно вызываются очень часто, иначе код плохо спроектирован.

С другой стороны, когда вы передаете объект как const ссылку, это внутренне (обычно) реализуется через указатель. Указатели можно всегда эффективно копировать на всех архитектурах.

В книге, которую я читаю, говорится, что функции-члены передают неявный параметр по ссылке...

Я думаю, что книга ошибается. Функции-члены классов неявно получают this указатель, который ссылается на текущий объект. Однако это указатель, и C++ запрещает его изменять. Нет причин передавать его по ссылке.

Только ли массив передается по ссылке в C++?

Массивы вообще редко передаются в C++ - обычно они передаются как указатели:

void foo(int[] x) { … }

это фактически то же самое, что

void foo(int* x) { … }

Компилятор обрабатывает эти два объявления одинаково. Когда вы попытаетесь вызвать любой из этих методов и передать ему массив x, C++ неявно преобразует массив в указатель на его первый элемент - это называется "распад". Так, foo(x) станет foo(&x[0]).

Однако массивы могут передаваться по ссылке, если задан их размер:

void foo(int (&x)[4]);

Но опять же, вы явно объявляете, что массив будет передаваться по ссылке.

7
ответ дан 18 December 2019 в 07:28
поделиться

Почему я не могу использовать Foo fInstance в классе Foo?

Потому что концептуально объекту Foo потребуется бесконечное количество места. Технически тип Foo неполон в определении Foo.

Что вам, вероятно, нужно, так это указатель на Foo как на член.

4
ответ дан 18 December 2019 в 07:28
поделиться

В C # объект передается «по ссылке», но, похоже, этого нет в C ++.

Нет, это неправильно, это распространенное заблуждение. В таких языках, как C #, VB и Java, переменные всегда передаются по значению (исключение явно передается как ref в C # или ByRef в VB).

Отличие от C ++ в том, что переменные не содержат самого объекта класса, они содержат только ссылку. Таким образом, методу передается не сам объект, а только его ссылка (а , что передается по значению).

Разница весьма существенная. Если C # использовал передачу по ссылке, следующий код распечатал бы другой результат:

void foo(string s) {
    s = "world";
}

string s = "hello";
foo(s);
Console.WriteLine(s); // prints "hello"
4
ответ дан 18 December 2019 в 07:28
поделиться

Разница между void DoSomething (const Foo & foo) и void DoSomething (Foo foo) - первый передает параметр по ссылке, а второй - по значению. Практические различия:

  1. Эффективность. Передача по значению может потребовать вызова конструктора копирования. Если конструктор копирования стоит дорого, передача по значению приведет к дополнительным накладным расходам.
  2. Применимость. Для передачи по значению требуется общедоступный конструктор копии. Если класс не поддерживает конструктор копирования, его нельзя передать по значению.
  3. Семантика. При передаче по ссылке вы не знаете, на кого можно ссылаться. Если базовый объект изменяется по какой-либо другой причине, значение ссылки изменится.

Чтобы объяснить №3 немного лучше, рассмотрим следующую ситуацию:

std::string global_string;

void foo(const std::string &str)
{
    if (str.empty())
    {
        global_string = "whatever";
        // is str still empty??
    }
}

Если foo вызывается как foo (global_string) , то при изменении global_string это также изменяется str .

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

По одному:

  1. doStuff(Foo f) означает, что при вызове метода в стеке будет создан новый объект Foo - AKA by-value. Вызов doStuff(const Foo &f) означает, что вы просто передаете новую ссылку, объект не дублируется, вы только держите ссылку на него. Это самый безопасный способ передачи аргументов, поскольку он не предполагает дублирования копии объекта. Это называется передачей по ссылке и является наиболее близким к поведению Java/C#.

  2. О каком неявном параметре вы говорите?

  3. Опять же, массивы (предполагается, что это std::arrays) могут передаваться по значению, указателю или ссылке - единого поведения не существует. Как упоминал Конард, массивы в стиле C (не более чем блоки памяти) не могут быть переданы по значению.

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

Передача по константной ссылке, а не по значению - не совсем приемлемая «хорошая практика».

Это сообщение в блоге объясняет, почему.

Люди склонны думать, что ссылка на константу выполняется быстрее, но правда в том, что компилятору разрешено оптимизировать копию при передаче по значению, поэтому передача по значению является хорошим вариантом по умолчанию (и действительно, стандартная библиотека обычно делает это. Например, std :: for_each принимает два итератора по значению и один функтор по значению )

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

Многие объекты представляют собой то, что нельзя или не следует копировать. Как правило, они имеют частный конструктор копии и должны передаваться по ссылке или константной ссылке на функции.

Другой причиной передачи по ссылке (константной или иной) может быть использование полиморфных объектов.Допустим, у вас есть базовый класс B и производный класс D . Вы можете безопасно передать объект типа D как const B & , но передача его по значению как объект типа B рискует ввести нарезку (только B подобъект копируется вместо всего объекта D ).

Поэтому хорошей практикой является передача по значению по умолчанию , но передача по константной ссылке определенно имеет свое место. Оба языка не зря.

0
ответ дан 18 December 2019 в 07:28
поделиться

В чем разница между void DoSomething(const Foo& foo) и void DoSomething(Foo foo)?

В широком смысле, последний будет глубоко копировать передаваемый аргумент (другими словами, он делает копию исходного объекта Foo). Первая делает поверхностную копию передаваемого аргумента (копируя его адрес в неизменяемую ссылку const, а не копируя сам объект Foo).

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

Может ли кто-нибудь дать мне пример неявного параметра и по ссылке? Я знаю, что если мы хотим передать объект по ссылке, нам нужно использовать & (например, Foo(Person& p) ), но как в C++ передать объект по ссылке для неявного параметра? Я читал, что неявный параметр в C++ выглядит как Contructor(string str) : strMemberVariable(str) {} ...

В контексте параметризованных унарных конструкторов (конструкторов, принимающих один аргумент), они могут быть неявными (по умолчанию) или явными.

class Foo
{
    Foo(int x) {...}
};

Это неявный. Это позволяет нам писать код типа:

Foo foo = 123;

void f(const Foo& x);
f(123);

В то время как это явное:

class Foo
{
    explicit Foo(int x) {...}
};

... и не будет соответствовать предыдущему коду. Предыдущий код должен быть изменен соответствующим образом:

Foo foo(123);

void f(const Foo& x);
f(Foo(123) );

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

Является ли массив единственной передачей по в C++?

Я не совсем понимаю, о чем здесь спрашивают, но массивы не могут передаваться по значению, если вы это имеете в виду. Мы можем передавать только ссылки/указатели на массивы:

// takes an array of 10 integers
void fn(int(&some_array)[10]);

// takes a pointer to an int array
void fn(int* some_array);

// takes a pointer to an int array (the 10 
// literal constant is ignored) and this function
// can likewise take any pointer to int
void fn(int some_array[10]);

Почему я не могу использовать Foo fInstance в Foo классе?

Это бесконечная рекурсия. Foo хранит fInstance, fInstance хранит другой fInstance, и так далее. Нет ничего, что могло бы остановить рекурсию, поэтому у вас просто будут объекты, хранящие объекты, хранящие объекты, хранящие объекты, хранящие объекты, и так далее, пока не закончится память. Таким образом, компиляторы обнаруживают это условие и запрещают его, поскольку никакое легитимное поведение во время выполнения не может быть результатом этого. Также не было бы способа определить размер Foo - это было бы бесконечное значение.

0
ответ дан 18 December 2019 в 07:28
поделиться
void DoSomething(Foo foo)

На самом деле передается копия foo, а

void DoSomething(Foo& foo)

Передается ссылка на foo, так что если вы измените foo в своей функции, вы измените оригинальное foo. Надеюсь, это имеет смысл.

Что касается массивов, то массив - это фактически указатель на начало массива, и этот указатель передается по кругу (весь массив не копируется).

array[5] = 0;//is the same as :
*(array+5) = 0; //this
-1
ответ дан 18 December 2019 в 07:28
поделиться

В книге, которую я читаю, сказано, что функции-члены передают неявный параметр по ссылке.

В книге говорится о неявном указателе this , который передается для каждой нестатической функции-члена, определенной в классе. Это потому, что C ++ хранит копию каждой функции-члена в классе не в каждом объекте, поэтому метод должен знать, с каким объектом этого класса он должен работать.

class FOO{
 int x;
 void doSomthing(int x);
}

void FOO::doSomething(int x){
  x = x;
}

будет скомпилирован во что-то вроде этого

void FOO::doSomething(FOO* this, int x){
  this->x = x;
}

Поскольку статические функции являются функциями класса, а не объектными функциями, им не нужно создавать объект для вызова, поэтому они не должны иметь доступа к не- static поля класса и, следовательно, не требует указателя this на объект.

0
ответ дан 18 December 2019 в 07:28
поделиться
  В чем разница между void DoSomething (const Foo & foo) и

void DoSomething (Foo foo)?

прагматически разницы нет, константа не даст вам изменить содержимое 'foo', тогда как передача по значению также не повлияет на содержимое аргумента, однако с точки зрения эффективности константа Foo & foo более эффективна, поскольку она не будет создавать копию, когда объект передается методу.

0
ответ дан 18 December 2019 в 07:28
поделиться

В чем разница между void DoSomething(const Foo& foo) и void DoSomething(Foo foo)

DoSomething(Foo foo) передает объект foo по значению, если Foo - примитивный тип данных, но по ссылке, если Foo - определяемый пользователем тип данных. Но во втором случае, если вы измените foo, это отразится на исходном объекте, что часто нежелательно. Об этом заботится DoSomething(const Foo& foo), которая передает foo по ссылке (таким образом, экономя дополнительные затраты памяти на передачу по значению) и все же не дает доступ на запись foo функции DoSomething. Таким образом, это лучшая практика.

Может ли кто-нибудь дать мне пример неявного параметра и по ссылке?

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

Является ли массив единственной передачей по ссылке в C++?

Нет, все объекты, определяемые пользователем, передаются по ссылке.

-5
ответ дан 18 December 2019 в 07:28
поделиться
Другие вопросы по тегам:

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