Как передать объекты в функции в C++?



Я новичок в программировании на C++, но у меня есть опыт в Java. Мне нужно руководство о том, как передавать объекты в функции в C++.



нужно ли передавать указатели, ссылки или значения без указателя и без ссылки? Я помню, что в Java нет таких проблем, так как мы передаем только переменную, которая содержит ссылку на объекты.



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

1153   7  

7 ответов:

правила для C++11:

передать по стоимости, за исключением

  1. вам не нужно владение объектом и простой псевдоним будет делать, в этом случае вы мимо const ссылка,
  2. вы должны мутировать объект, в этом случае используйте проходим мимо, неconst ссылка lvalue,
  3. вы передаете объекты производные классы как базовые классы, в этом случае вам нужно проходим по ссылке. (Использовать предыдущие правила, чтобы определить, следует ли пройти const ссылка или нет.)

передача указателя практически никогда не рекомендуется. Необязательные параметры лучше всего выражаются как boost::optional, и сглаживание выполняется отлично по ссылке.

семантика перемещения C++11 делает передачу и возврат по значению намного более привлекательными даже для сложных объекты.


правила для C++03:

аргументов переходят by const ссылка, за исключением

  1. они должны быть изменены внутри функции, и такие изменения должны быть отражены снаружи, и в этом случае вы проходим мимо неconst ссылка
  2. функции должен быть вызван без каких-либо аргументов, в этом случае вы проходите мимо указатель, так что пользователи могут передать NULL/0/; применить предыдущее правило, чтобы определить, следует ли пройти мимо указателя на const аргумент
  3. они имеют встроенные типы, которые могут быть передает копия
  4. они должны быть изменены внутри функции, и такие изменения должны не быть отражены снаружи, в этом случае вы можете pass by copy (альтернативой было бы пройти по предыдущим правилам и сделать копию внутри функции)

(здесь "pass by value "называется" pass by copy", потому что передача по значению всегда создает копию в C++03)


это еще не все, но эти несколько правил новичка помогут вам довольно далеко.

существуют некоторые различия в соглашениях о вызовах в C++ и Java. В C++ есть технически говоря только два соглашения: pass-by-value и pass-by-reference, с некоторой литературой, включая третье соглашение pass-by-pointer (то есть фактически pass-by-value типа указателя). Кроме того, вы можете добавить постоянство к типу аргумента, улучшая семантику.

проходим по ссылке

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

передать по значению (и указатель)

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

специальная версия передав указатель (адрес объекта) в функцию. Функция получает указатель, и все операции, примененные к самому указателю, применяются к копии (указателю), с другой стороны, операции, примененные к разыменованному указателю, будут применяться к экземпляру объекта при этом место памяти, поэтому функция может иметь побочные эффекты. Эффект использования pass-by-value указателя на объект позволит внутренней функции изменять внешние значения, как и в случае с pass-by-reference, а также позволит использовать необязательные значения (передать нулевой указатель).

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

добавление const в уравнение

в C++ вы можете назначать константы объектам при определении переменных, указателей и ссылок на разных уровнях. Вы можете объявить переменную постоянной, вы можете объявить ссылку на постоянный экземпляр, и вы можете определить все указатели на постоянные объекты, постоянные указатели на изменяемые объекты и константы указатели на постоянные элементы. И наоборот, в Java вы можете определить только один уровень константы (ключевое слово final): уровень переменной (экземпляр для примитивных типов, ссылка для ссылочных типов), но вы не можете определить ссылку на неизменяемый элемент (если сам класс не является неизменяемым).

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

правила

Это некоторые основные правила, чтобы следовать:

  • предпочитаю pass-by-value для примитивные типы
  • предпочитаю pass-by-reference со ссылками на константу для других типов
  • если функция должна изменить аргумент, используйте pass-by-reference
  • если аргумент является необязательным, используйте pass-by-pointer (to constant, если необязательное значение не должно быть изменено)

есть и другие небольшие отклонения от этих правил, первым из которых является обращение собственности на объект. Когда объект динамически выделяется с помощью new, он должен быть освобожден с помощью delete (или его версий []). Объект или функция, ответственная за уничтожение объекта, считается владельцем ресурса. Когда динамически выделенный объект создается в куске кода, но право собственности передается другому элементу, это обычно делается с помощью семантики сквозных указателей или, если это возможно, с помощью интеллектуальных указателей.

Примечание

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

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

функция подкачки выше изменения оба его аргумента с помощью ссылок. Ближайший код в Java:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

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

есть несколько случаев, чтобы рассмотреть.

параметр изменен (параметры" out "и" in/out")

void modifies(T &param);
// vs
void modifies(T *param);

этот случай в основном о стиле: вы хотите, чтобы код выглядел как вызов (obj) или вызов (&obj)? Однако есть две точки, где разница имеет значение: необязательный случай, ниже, и вы хотите использовать ссылку при перегрузке операторов.

...и необязательно

void modifies(T *param=0);  // default value optional, too
// vs
void modifies();
void modifies(T &param);
не изменено
void uses(T const &param);
// vs
void uses(T param);

это интересный случай. Эмпирическое правило "дешево копировать" типы передаются по значению-это обычно небольшие типы (но не всегда) - в то время как другие передаются по const ref. Однако, если вам нужно сделать копию в вашей функции независимо, вы должно пройти по значению. (Да, это раскрывает немного деталей реализации. C'est le C++.)

...и необязательно

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // or optional(T param)

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

Const по значению-это деталь реализации

void f(T);
void f(T const);

эти объявления на самом деле точно такая же функция! при передаче по значению const-это чисто деталь реализации. попробуйте:

void f(int);
void f(int const) { /* implements above function, not an overload */ }

typedef void NC(int);       // typedefing function types
typedef void C(int const);

NC *nc = &f;  // nc is a function pointer
C *c = nc;    // C and NC are identical types

передать по значению:

void func (vector v)

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

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

пройти по ссылке const:

void func (const vector & v);

эта форма эмулирует pass-by-value поведение при удалении накладных расходов копирования. Функция получает доступ к исходному объекту, но не может изменить его значение.

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

пройти по неконстантной ссылке:

void func (vector & v)

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

как и в случае ссылки const, это не является потокобезопасным.

пройти мимо указателя const:

void func (const vector * vp);

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

не потокобезопасными.

пройти мимо неконстантного указателя:

void func (vector * vp);

похоже на неконстантную ссылку. Звонящий, как правило, устанавливает переменная NULL, когда функция не должна записывать обратно значение. Это соглашение рассматривается во многих API glibc. Пример:

void func (string * str, /* ... */) {
    if (str != NULL) {
        *str = some_value; // assign to *str only if it's non-null
    }
}

так же, как все проходят по ссылке/указателю, а не потокобезопасно.

Class CPlusPlusJavaFunctionality {
    public:
       CPlusPlusJavaFunctionality(){
         attribute = new int;
         *attribute = value;
       }

       void setValue(int value){
           *attribute = value;
       }

       void getValue(){
          return *attribute;
       }

       ~ CPlusPlusJavaFuncitonality(){
          delete(attribute);
       }

    private:
       int *attribute;
}

void changeObjectAttribute(CPlusPlusJavaFunctionality obj, int value){
   int* prt = obj.attribute;
   *ptr = value;
}

int main(){

   CPlusPlusJavaFunctionality obj;

   obj.setValue(10);

   cout<< obj.getValue();  //output: 10

   changeObjectAttribute(obj, 15);

   cout<< obj.getValue();  //output: 15
}

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

существует три способа передачи объекта в функцию в качестве параметра:

  1. проходим по ссылке
  2. передать по значению
  3. добавление константы в параметр

пройдите через следующий пример:

class Sample
{
public:
    int *ptr;
    int mVar;

    Sample(int i)
    {
        mVar = 4;
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }

    void PrintVal()
    {
        cout << "The value of the pointer is " << *ptr << endl
             << "The value of the variable is " << mVar;
   }
};

void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}


int main()
{

  Sample s1= 10;
  SomeFunc(s1);
  s1.PrintVal();
  char ch;
  cin >> ch;
}

выход:

скажем, я нахожусь в someFunc
Значением указателя является -17891602
Значение переменной равно 4

ниже приведены способы передачи аргументов / параметров для работы в C++.

1. по значению.

// passing parameters by value . . .

void foo(int x) 
{
    x = 6;  
}

2. по ссылке.

// passing parameters by reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  
}

// passing parameters by const reference . . .

void foo(const int &x) // x is a const reference
{
    x = 6;  // compile error: a const reference cannot have its value changed!
}

3. по объектам.

class abc
{
    display()
    {
        cout<<"Class abc";
    }
}


// pass object by value
void show(abc S)
{
    cout<<S.display();
}

// pass object by reference
void show(abc& S)
{
    cout<<S.display();
}

Comments

    Ничего не найдено.