Вопрос

РЕДАКТИРОВАТЬ:Этот вопрос больше касается языковой инженерии, чем самого C++.Я использовал C++ в качестве примера, чтобы показать, чего я хочу, главным образом потому, что использую его ежедневно.Я не хотел знать, как это работает на C++, но открыл дискуссию о том, как это работает. мог быть сделано.

Сейчас это работает не так, я так делаю желание это можно сделать, и это наверняка нарушит совместимость C, но я думаю, что в этом и заключается суть extern "C".

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

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

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, оператор ссылки кажется мне способом получить переменную адрес, именно так я получал указатели.Я имею в виду, что это один и тот же оператор, но с разной семантикой в ​​разных контекстах, не кажется ли вам это тоже немного неправильным?

Это было полезно?

Решение

Я думаю, вы упускаете суть C++ и семантики C++.Вы упустили тот факт C++ корректно передает (почти) все по значению, потому что в C это делается так же.Всегда.Но не только в C, как я покажу вам ниже...

Семантика параметров на C

В C все передается по значению.«Примитивы» и «POD» передаются путем копирования их значений.Измените их в своей функции, и оригинал не будет изменен.Тем не менее, стоимость копирования некоторых POD может быть нетривиальной.

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

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->value равно 32.Потому что p был передан путем копирования значения адреса.Таким образом, исходный p не был изменен (и появился новый).

Семантика параметров в Java и C Sharp

Кого-то может удивить, но в Java тоже все копируется по значению.Приведенный выше пример C даст точно такие же результаты в Java.Это почти то, что вам нужно, но вы не сможете передать примитив «по ссылке/указателю» так же легко, как в C.

В C# они добавили ключевое слово «ref».Он работает более или менее похоже на ссылку в C++.Дело в том, что в C# вы должны упоминать это как в объявлении функции, так и при каждом вызове.Думаю, это не то, чего ты хочешь, опять же.

Семантика параметров в C++

В C++ почти все передается путем копирования значения.Когда вы не используете ничего, кроме типа символа, вы копируете символ (как это делается в C).Вот почему, когда вы используете *, вы передаете копию адреса символа.

Но когда вы используете &, предположим, что вы передаете реальный объект (будь то структура, int, указатель и т. д.):Ссылка.

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

Правда в том, что ссылка — это больше, чем просто синтаксический сахар.

  • В отличие от указателей, он позволяет манипулировать объектом, как если бы он находился в стеке.
  • Нестроечные указатели, связанные с ключевым словом const, разрешают неявное преобразование из одного типа в другой (в основном через конструкторы).
  • В отличие от указателей, символ не должен быть NULL/недействительным.
  • В отличие от «копирования», вы не тратите бесполезное время на копирование объекта.
  • В отличие от «по копированию», вы можете использовать его как параметр [out]
  • В отличие от «копирования», вы можете использовать весь спектр ООП в 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++ — это передача по копированию/значению, потому что именно так все работает, будь то C, C# или Java (даже в JavaScript...:-п ...).Как и в C#, в C++ есть ссылочный оператор/ключевое слово, в качестве бонуса.

Насколько я понимаю, вы, возможно, делаете то, что я называю полушутя С+, то есть C с некоторыми ограниченными возможностями C++.

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

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

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

Редактировать

Вопрос был в некотором роде изменено (см. https://stackoverflow.com/revisions/146271/list ).Я оставлю свой первоначальный ответ и отвечу на новые вопросы ниже.

Что вы думаете о семантике передачи по ссылке по умолчанию в C++? Как вы сказали, это нарушит совместимость, и у вас будет другой проход для примитивов (т.встроенные типы, которые по-прежнему будут передаваться путем копирования) и структуры/объекты (которые будут передаваться как ссылки).Вам придется добавить еще один оператор, обозначающий «передачу по значению» (внешний «C» довольно ужасен и уже используется для чего-то совершенно другого).Нет, мне очень нравится, как это делается сегодня в C++.

[...] оператор ссылки кажется мне способом получить адрес переменной, именно такой способ я использовал для получения указателей.Я имею в виду, что это один и тот же оператор, но с разной семантикой в ​​разных контекстах, не кажется ли вам это тоже немного неправильным? Да и нет.Оператор >> также изменил свою семантику при использовании с потоками C++.Затем вы можете использовать оператор += для замены strcat.Я предполагаю, что оператор & использовался потому, что его значение «противоположно указателю», и потому что они не хотели использовать еще один символ (ASCII ограничен, а оператор области действия::а также указатель -> показывает, что можно использовать лишь несколько других символов).Но теперь, если & вас беспокоит, && действительно вас расстроит, поскольку в C++0x добавлен унарный && (своего рода суперссылка...).Я еще сам это не переварил...

Другие советы

Опция компилятора, которая полностью меняет смысл раздела кода, кажется мне очень плохой идеей.Либо используйте синтаксис C++, либо найдите другой язык.

Я бы предпочел больше не злоупотреблять ссылками, делая каждый (неполный) параметр ссылкой.

Основная причина, по которой ссылки были добавлены в C++, заключалась в том, что для поддержки перегрузки операторов;если вам нужна семантика «передачи по ссылке», у C есть вполне разумный способ сделать это:указатели.

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

Также см

Я хочу изменить аргумент, должен ли я использовать указатель или использовать ссылку? Я не знаю сильной логической причины.При передаче `` не объект '' (например,Нулевый указатель) приемлем, используя указатель имеет смысл.Мой личный стиль состоит в том, чтобы использовать указатель, когда я хочу изменить объект, потому что в некоторых контекстах это облегчает то, что модификация возможно.

из того же FAQ.

Да, я считаю, что это довольно запутанная перегрузка.

Вот что Microsoft говорит о ситуации:

Не путайте ссылочные объявления с использованием оператора адреса.Если перед идентификатором & стоит тип, например int или char, идентификатор объявляется как ссылка на тип.Если перед идентификатором & не указан тип, используется оператор адреса.

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

Лучший совет — выработать привычку думать о том, чего вы действительно хотите.Передача по ссылке удобна, когда у вас нет конструктора копирования (или вы не хотите его использовать), и дешевле для больших объектов.Однако тогда изменения параметра ощущаются за пределами класса.Вместо этого вы могли бы пройти мимо const ссылка - тогда мутаций нет, но вы не можете вносить локальные изменения.Передавайте константное по значению для дешевых объектов, которые должны быть доступны только для чтения в функции, и передайте неконстантное по значению, если вам нужна копия, в которую можно внести локальные изменения.

Каждая перестановка (по значению/по ссылке и константная/неконстантная) имеет важные различия, которые определенно не эквивалентны.

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

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

Кодовая база, полностью корректная для 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++ вводит в заблуждение. Все передается по значению.У вас есть три случая:

  1. Где вы передаете локальную копию переменной

    void myFunct(int cantChangeMyValue)
    
  2. Где вы передаете локальную копию указателя на переменную

    void myFunct(int* cantChangeMyAddress) {
        *cantChangeMyAddress = 10;
    }
    
  3. Когда вы передаете «ссылку», но с помощью магии компилятора это все равно, как если бы вы передали указатель и просто каждый раз разыменовывали его.

    void myFunct(int & hereBeMagic) {
        hereBeMagic = 10; // same as 2, without the dereference
    }
    

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

То, что вы предлагаете, не позволит программисту сделать номер 1.Я лично считаю, что было бы плохой идеей отказаться от этой опции.Одним из основных преимуществ C/C++ является возможность детального управления памятью.Передача всего по ссылке — это просто попытка сделать C++ более похожим на Java.

есть что-то непонятное.Когда ты говоришь:

int b(b &param);

что вы имели в виду для второго «б»?ты забыл указать тип?ты забыл написать по-другому относительно первой "б"?Вам не кажется, что логично написать что-то вроде:

class B{/*something...*/};
int b(B& param);

Поскольку теперь я предполагаю, что вы имеете в виду то, что я пишу.

Теперь ваш вопрос: «Не думаете ли вы, что будет лучше, если компилятор будет рассматривать каждое передаваемое значение не-POD как передаваемое по ссылке?».Первая проблема в том, что это нарушит ваш контракт.Я полагаю, вы имеете в виду передачу по CONST-ссылке, а не только по ссылке.

Ваш вопрос теперь сводится к следующему:«Знаете ли вы, есть ли какая-нибудь директива компилятора, которая может оптимизировать вызов функции по значению?»

Теперь ответ: «Я не знаю».

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

Отслеживание всех вызовов конструкторов копирования, всех перегруженных разыменований и т. д. быстро выходит из-под контроля.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top