Семантика ссылок по умолчанию в C++ [закрыто]
EDIT: этот вопрос больше касается языковой инженерии, чем самого C++. Я использовал C++ в качестве примера, чтобы показать, что я хотел, в основном потому, что я использую его ежедневно. Я не хотел знать, как это работает на C++, но открыть дискуссию о том, как это может быть сделано.
это не так, как это работает прямо сейчас, вот так я желаю Это можно сделать, и это наверняка нарушит совместимость C, но это то, что я думаю, что extern " C " - это все о.
Я имею в виду, что в каждой функции или методе, которые вы объявляете прямо сейчас, вы должны явно написать, что объект будет отправлен путем префикса ссылки на него. Я хочу, чтобы каждый не-POD тип автоматически отправлялся по ссылке, потому что я использую это много, на самом деле для каждого объекта, размер которого превышает 32 бита, и это почти каждый мой класс.
давайте проиллюстрируем, как это прямо сейчас, предположим a, b и c для занятия:
class example { public: int just_use_a(const a &object); int use_and_mess_with_b(b &object); void do_nothing_on_c(c object); };
теперь, что я хочу:
class example { public: int just_use_a(const a object); int use_and_mess_with_b(b object); extern "C" void do_nothing_on_c(c object); };
теперь do_nothing_on_c () может вести себя так же, как сегодня.
Это было бы интересно, по крайней мере для меня, чувствует себя намного более четкой, а также если вы знаю каждый параметр не-POD приходит по ссылке я считаю, что ошибки будут такими же, что если бы вам пришлось явно объявить его.
еще одна точка зрения на это изменение, от кого-то из C, ссылочный оператор кажется мне способом получить переменную адрес, так я использовал для получения указателей. Я имею в виду, что это один и тот же оператор, но с разной семантикой в разных контекстах, разве это не кажется вам немного неправильным?
9 ответов
Я думаю, вам не хватает точки C++ и семантики C++. Вы упустили этот факт C++ корректен в передаче (почти) всего по значению, потому что это так, как это делается в C. всегда. Но не только в C, как я покажу ниже...
семантика параметров на C
В C, все передается по значению. "примитивы" и "стручки" передаются путем копирования их значения. Измените их в своей функции, и оригинал не будет изменен. Тем не менее, стоимость копирование некоторых стручков может быть нетривиальным.
когда вы используете обозначение указателя ( * ), вы не проходите по ссылке. Вы передаете копию адреса. Что более или менее одно и то же, но с одним тонким различием:
typedef struct { int value ; } P ;
/* p is a pointer to P */
void doSomethingElse(P * p)
{
p->value = 32 ;
p = malloc(sizeof(P)) ; /* Don't bother with the leak */
p->value = 45 ;
}
void doSomething()
{
P * p = malloc(sizeof(P)) ;
p->value = 25 ;
doSomethingElse(p) ;
int i = p->value ;
/* Value of p ? 25 ? 32 ? 42 ? */
}
конечное значение p - > значение равно 32. Потому что p был передан путем копирования значения адреса. Таким образом, исходный p не был изменен (и новый был просочился).
семантика параметров на Java и C Sharp
Это может быть удивительно для некоторых, но в Java, все копируется по значению, тоже. Приведенный выше пример C даст точно такие же результаты в Java. Это почти то, что вы хотите, но вы не сможете пройти примитивный "по ссылке/указателю" так же легко, как в с.
в C# они добавили ключевое слово "ref". Он работает более или менее как ссылка в C++. Дело в том, что на C# вы должны упомянуть об этом как в объявлении функции, так и при каждом вызове. Я думаю, это не то, что вы хотите, опять же.
семантика параметров на C++
В C++ почти все передается путем копирования значения. Когда вы используете только тип символа, вы копируете символ (как это делается в C). Вот почему, когда вы используете*, вы передаете копию адреса символа.
но когда вы используете&, предположим, что вы передаете реальный объект (будь то struct, int, pointer, whatever): ссылка.
Это легко принять его за синтаксический сахар (т. е. за кулисами он работает как указатель, а сгенерированный код используется для указателя). Но...
правда в том, что ссылка больше, чем синтаксический сахар.
- в отличие от указателей, он разрешает манипулировать объектом, как будто на стеке.
- Unline указатели, когда associatied с ключевого слова const, она разрешает скрытую рекламу из одного типа в другой (через конструкторы, в основном.)
- в отличие от указателей, символ не должен быть NULL / invalid.
- в отличие от" by-copy", вы не тратите бесполезное время на копирование объекта
- в отличие от" by-copy", вы можете использовать его в качестве параметра [out]
- в отличие от" by-copy", вы можете использовать полный диапазон ООП в C++ (т. е. вы передаете полный объект функции, ожидающей интерфейса).
Итак, ссылки имеют лучшее из обоих миры.
давайте посмотрим пример C, но с вариацией C++ на функции doSomethingElse:
struct P { int value ; } ;
// p is a reference to a pointer to P
void doSomethingElse(P * & p)
{
p->value = 32 ;
p = (P *) malloc(sizeof(P)) ; // Don't bother with the leak
p->value = 45 ;
}
void doSomething()
{
P * p = (P *) malloc(sizeof(P)) ;
p->value = 25 ;
doSomethingElse(p) ;
int i = p->value ;
// Value of p ? 25 ? 32 ? 42 ?
}
результат 42, и старый p просочился, замененный новым p. Потому что, в отличие от кода C, мы передаем не копию указателя, а ссылку на указатель, то есть сам указатель.
при работе с C++, приведенный выше пример должен быть кристально чистой. Если это не так, то вы упускаете что-то.
вывод
C++ - это pass-by-copy / value, потому что так все работает, будь то на C, на C# или на Java (даже в JavaScript... :- p ...). И, как и C#, C++ имеет ссылочный оператор / ключевое слово,в качестве бонуса.
сейчас, насколько я понимаю, вы, возможно, делаете то, что я называю половина-jockingly C+, то есть, C с общества с++.
возможно, ваше решение использует typedefs (тем не менее, это приведет в ярость ваших коллег по C++, чтобы увидеть код, загрязненный бесполезными typedefs...), но это только запутает тот факт, что вам действительно не хватает C++.
Как сказано в другом посте, вы должны изменить свое мышление от разработки C (чего угодно) до разработки C++, или, возможно, вам следует перейти на другой язык. Но не продолжайте программировать путь C с помощью функций c++, потому что, сознательно игнорируя / запутывая силу используемых вами идиом, вы создадите неоптимальный код.
Примечание: и не проходите мимо копирования ничего, кроме примитивов. Вы кастрируете свою функцию из ее емкости OO, и в C++ это не то, что вы хотите.
редактировать
вопрос несколько изменено (см. https://stackoverflow.com/revisions/146271/list). Я даю свой первоначальный ответ и отвечаю на новые вопросы ниже.
что вы думаете о default семантика pass-by-reference на C++? как вы сказали, это нарушит совместимость, и у вас будет другой проход для примитивов (т. е. встроенных типов, которые все равно будут передаваться копией) и структур/объектов (которые будут передаваться как ссылки). Вам нужно будет добавить другой оператор, чтобы означать " pass-by-value "(extern" C " довольно ужасен и уже используется для чего-то другого). Нет, мне очень нравится, как это делается сегодня на C++.
[...] этот оператор reference кажется мне способом получить адрес переменной, именно так я использовал для получения указателей. Я имею в виду, что это один и тот же оператор, но с разной семантикой в разных контекстах, разве это не кажется вам немного неправильным? да и нет. Оператор > > изменил свою семантику при использовании с потоками c++. Затем вы можете использовать operator += для замены strcat. Я думаю, оператор & привык, потому что его значение как "противоположность указателя" , и потому что они не хотели использовать еще один символ (ASCII ограничен, а оператор scope::, а также указатель - > показывает, что можно использовать несколько других символов). Но теперь, если & беспокоит вас, & & действительно расстроит вас, поскольку они добавили унарный && в C++0x (своего рода супер-ссылку...). Я сам еще не переварил...
опция компилятора, которая полностью меняет значение раздела кода, звучит для меня как очень плохая идея. Либо используйте синтаксис C++, либо найдите другой язык.
Я бы предпочел больше не злоупотреблять ссылками, делая каждый (неквалифицированный) параметр ссылкой.
основной причиной добавления ссылок на C++ было для поддержки перегрузки операторов; если вы хотите семантику "pass-by-reference", у C был вполне разумный способ сделать это: указатели.
использование указателей проясняет ваше намерение изменить значение указываемого объекта, и это можно увидеть, просто посмотрев на вызов функции, вы не нужно смотреть на объявление функции, чтобы увидеть, использует ли она ссылку.
Также см.
я хочу изменить аргумент, должен ли я использовать указатель или должен использовать ссылку? Я не знаю сильный логическая причина. Если проходящий `не объект " (например, нулевой указатель) приемлемо, используя указатель делает чувство. Мой личный стиль-использовать указатель, когда я хочу изменить объект, потому что в некоторых контекстах делает легче заметить, что модификация возможна.
Да, я считаю, что это довольно запутанным перегрузки.
Это то, что microsoft должна сказать о ситуации:
Не путайте ссылочные объявления с использованием адреса оператора. Когда & identifier предшествует типу, например int или char, идентификатор объявляется как ссылка на тип. Если & identifier не предшествует типу, используется оператор address-of.
Я не действительно здорово на C или C++, но у меня больше головных болей, сортируя различные виды использования * и & на обоих языках, чем я кодирую в ассемблере.
лучший совет-сделать привычку думать о том, что вы действительно хотите, чтобы произошло. Передача по ссылке хороша, когда у вас нет конструктора копирования (или не хотите его использовать), и это дешевле для больших объектов. Однако затем мутации параметра ощущаются вне класса. Вы могли бы вместо этого пройти мимо const
ссылка -- тогда нет мутаций, но вы не можете вносить локальные изменения. Pass const by-value для дешевых объектов, которые должны быть доступны только для чтения в функции и передайте не-const по-значению, если вам нужна копия, в которую вы можете внести локальные изменения.
каждая перестановка (по значению/по ссылке и const/non-const) имеет важные различия, которые определенно не эквивалентны.
когда вы передаете по значению, вы копируете данные в стек. В случае, если у вас есть оператор= определенный для структуры или класса, который вы передаете, он выполняется. Я не знаю никакой директивы компилятора, которая смыла бы суету неявной языковой путаницы, которую предлагаемое изменение неизбежно вызовет.
обычно рекомендуется передавать значения по ссылке const, а не только по ссылке. Это гарантирует, что значение не может быть изменено в вызывающая функция. Это один из элементов кодовой базы const-correct.
полностью const-правильная кодовая база идет еще дальше, добавляя const в конец прототипов. Подумайте:
void Foo::PrintStats( void ) const {
/* Cannot modify Foo member variables */
}
void Foo::ChangeStats( void ) {
/* Can modify foo member variables */
}
Если вы должны были передать объект Foo в функцию с префиксом const, вы можете вызвать PrintStats(). Компилятор выдаст ошибку при вызове ChangeStats ().
void ManipulateFoo( const Foo &foo )
{
foo.PrintStats(); // Works
foo.ChangeStats(); // Oops; compile error
}
Я честно думаю, что вся эта передача по значению/передача по ссылочной идее в C++ вводит в заблуждение. все проходит по значению. У вас три случая:--6-->
-
где вы передаете локальную копию переменной
void myFunct(int cantChangeMyValue)
-
где вы передаете локальную копию указателя на переменную
void myFunct(int* cantChangeMyAddress) { *cantChangeMyAddress = 10; }
-
где вы передаете "ссылку", но через магию компилятора это так же, как если бы вы передали указатель и просто разыграли его каждый раз.
void myFunct(int & hereBeMagic) { hereBeMagic = 10; // same as 2, without the dereference }
Я лично считаю, что гораздо менее запутанным помнить, что все проходит по стоимости. В некоторых случаях это значение может быть адресом, который позволяет изменять вещи вне функции.
что вы предлагаете, не позволит программисту сделать номер 1. Я лично думаю, что было бы плохой идеей отказаться от этого варианта. Один из главных плюсов C / C++ имеет мелкозернистую память управление. Заставить все пройти по ссылке просто пытается сделать C++ более похожим на Java.
есть что-то не понятно. когда вы говорите:
int b (b ¶m);
что вы намеревались сделать для второй "Б"? вы забыли ввести тип? вы забыли написать иначе по отношению к первой букве "б"? вы не думаете, что это ясно, чтобы написать что-то вроде:
class B{/*something...*/};
int b(B& param);
С этого момента, я полагаю, вы имеете в виду то, что я пишу.
теперь ваш вопрос: "не думаете ли вы, что будет лучше, если компилятор рассмотрим каждое значение пропуска не-POD как pass-by-ref?". Первая проблема в том, что это разорвет ваш контракт. Я предполагаю, что вы имеете в виду проходную ссылку, а не только ссылку.
теперь ваш вопрос сводится к следующему: "знаете ли вы, есть ли директива компиляторов, которая может оптимизировать вызов функции по значению?"
ответ теперь "я не знаю".
Я думаю, что c++ станет очень грязным, если вы начнете смешивать все доступные параметры с их вариациями const.
Он быстро выходит из-под контроля, чтобы отслеживать все вызовы конструкторов копирования, все разыменования перегружены и так далее.