C ++에서 참조로 포인터를 전달하는 이유?


98

어떤 상황에서 C ++에서 이러한 성격의 코드를 사용하고 싶습니까?

void foo(type *&in) {...}

void fii() {
  type *choochoo;
  ...
  foo(choochoo);
}

포인터를 반환해야하는 경우-반환 값을 사용하는 것이 좋습니다
littleadv

6
이유를 설명해 주시겠습니까? 이 칭찬은별로 도움이되지 않습니다. 제 질문은 상당히 합법적입니다. 이것은 현재 프로덕션 코드에서 사용되고 있습니다. 이유를 완전히 이해하지 못합니다.
Matthew Hoggan 2012

1
David는 그것을 아주 잘 요약합니다. 포인터 자체가 수정되고 있습니다.
chris

2
함수에서 "New"연산자를 호출 할 경우이 방법을 전달합니다.
NDEthos

답변:


143

포인터가 가리키는 개체가 아닌 포인터를 수정해야하는 경우 참조로 포인터를 전달하는 것이 좋습니다.

이것은 이중 포인터가 사용되는 이유와 유사합니다. 포인터에 대한 참조를 사용하는 것이 포인터를 사용하는 것보다 약간 더 안전합니다.


14
참조에 의한 전달 의미론에 대한 간접 수준이 하나 더 적다는 점을 제외하면 포인터의 포인터와 비슷합니까?

때로는 참조로 포인터를 반환하고 싶을 때도 있습니다. 예를 들어 동적으로 할당 된 배열에 데이터를 보유하는 클래스가 있지만이 데이터에 대한 (비상시) 액세스를 클라이언트에 제공하려는 경우입니다. 동시에 클라이언트가 포인터를 통해 메모리를 조작하는 것을 원하지 않습니다.

2
@William에 대해 자세히 설명해 주시겠습니까? 흥미롭게 들리 네요. 해당 클래스의 사용자가 내부 데이터 버퍼에 대한 포인터를 가져 와서 읽고 쓸 수 있지만, 예를 들어 해당 버퍼를 사용하여 해당 버퍼를 해제 delete하거나 메모리의 다른 위치에 다시 바인딩 할 수 없다는 의미 입니까? 아니면 내가 틀렸습니까?
BarbaraKwarc

2
@BarbaraKwarc 물론입니다. 동적 배열의 간단한 구현이 있다고 가정합니다. 당연히 []운영자에게 과부하가 걸립니다. 이에 대한 가능한 (실제로 자연스러운) 서명 중 하나는 const T &operator[](size_t index) const. 그러나 T &operator[](size_t index). 동시에 둘 다 가질 수 있습니다. 후자는 myArray[jj] = 42. 동시에 데이터에 대한 포인터를 제공하지 않으므로 호출자가 메모리를 엉망으로 만들 수 없습니다 (예 : 실수로 삭제).

오. 나는 당신이 포인터에 대한 참조를 반환하는 것에 대해 이야기하고 있다고 생각했습니다. 버퍼 자체를 조작 (예 : 해제)하지 않고 버퍼에 대한 읽기 / 쓰기 액세스를 제공합니다. 평범한 오래된 참조를 반환하는 것은 다른 일이며 나는 그것을 알고 있습니다.
BarbaraKwarc

65

C ++ 프로그래머의 50 %는 삭제 후 포인터를 null로 설정하는 것을 좋아합니다.

template<typename T>    
void moronic_delete(T*& p)
{
    delete p;
    p = nullptr;
}

참조가 없으면 호출자에게 영향을주지 않고 포인터의 로컬 복사본 만 변경합니다.


19
나는 이름을 좋아한다; )
4pie0 2014 년

2
나는 답을 알아 내기 위해 어리석게 들리는 거래를 할 것입니다. 내가 무엇을 놓치고 있습니까?
Sammaron 2015 년

1
@Sammaron 역사를 보면 함수 템플릿이라고 불렸지만 paranoid_deletePuppy는 이름을 moronic_delete. 그는 아마도 다른 50 %에 속할 것입니다;) 어쨌든 짧은 대답은 null after 설정 포인터 delete가 거의 유용하지 않으며 (어쨌든 범위를 벗어나야하기 때문에) 종종 "사용 후 사용"버그의 탐지를 방해한다는 것입니다.
fredoverflow

@fredoverflow 귀하의 답변을 이해하지만 @Puppy는 delete일반적으로 어리석은 것 같습니다 . 강아지, 인터넷 검색을 좀 해봤는데, 삭제가 왜 완전히 쓸모가 없는지 알 수 없습니다. 조금 더 설명하거나 링크를 제공 할 수 있습니까?
Sammaron 2015 년

@Sammaron, C ++에는 이제 일반 포인터를 캡슐화하고 범위를 벗어날 때 자동으로 "delete"를 호출하는 "스마트 포인터"가 있습니다. 스마트 포인터는이 문제를 완전히 제거하기 때문에 C 스타일 덤 포인터보다 훨씬 선호됩니다.
tjwrona1992

14

David의 대답은 맞지만 여전히 약간 추상적 인 경우 두 가지 예가 있습니다.

  1. 메모리 문제를 더 일찍 발견하기 위해 해제 된 모든 포인터를 제로화 할 수 있습니다. C 스타일 :

    void freeAndZero(void** ptr)
    {
        free(*ptr);
        *ptr = 0;
    }
    
    void* ptr = malloc(...);
    
    ...
    
    freeAndZero(&ptr);

    C ++에서 동일한 작업을 수행하려면 다음을 수행 할 수 있습니다.

    template<class T> void freeAndZero(T* &ptr)
    {
        delete ptr;
        ptr = 0;
    }
    
    int* ptr = new int;
    
    ...
    
    freeAndZero(ptr);
  2. 연결 목록을 다룰 때-종종 단순히 다음 노드에 대한 포인터로 표시됩니다.

    struct Node
    {
        value_t value;
        Node* next;
    };

    이 경우 빈 목록에 삽입 할 때 결과가 NULL더 이상 포인터 가 아니므로 들어오는 포인터를 반드시 변경해야합니다 . 이것은 함수에서 외부 포인터를 수정하여 서명에 포인터에 대한 참조를 포함하는 경우입니다.

    void insert(Node* &list)
    {
        ...
        if(!list) list = new Node(...);
        ...
    }

이 질문에 예가 있습니다 .


8

전달 된 포인터에 메모리를 할당하고 크기를 반환하는 함수를 제공하기 위해 이와 같은 코드를 사용해야했습니다. 내 회사가 STL을 사용하여 나에게 "객체"를 제공하기 때문입니다.

 int iSizeOfArray(int* &piArray) {
    piArray = new int[iNumberOfElements];
    ...
    return iNumberOfElements;
 }

좋지는 않지만 포인터는 참조로 전달되어야합니다 (또는 이중 포인터 사용). 그렇지 않은 경우, 메모리 누수가 발생하는 값으로 전달되면 메모리가 포인터의 로컬 사본에 할당됩니다.


2

한 가지 예는 파서 함수를 작성하고 읽을 소스 포인터를 전달하는 경우입니다. 함수가 파서가 올바르게 인식 한 마지막 문자 뒤에 해당 포인터를 앞으로 밀어 넣어야하는 경우입니다. 포인터에 대한 참조를 사용하면 함수가 원래 포인터를 이동하여 위치를 업데이트 함을 분명히 알 수 있습니다.

일반적으로 포인터를 함수에 전달 하고 원본에 영향을주지 않고 복사본을 이동하는 대신 원본 포인터를 다른 위치 로 이동 시키려면 포인터에 대한 참조를 사용 합니다.


왜 반대 투표입니까? 내가 쓴 내용에 문제가 있습니까? 설명할까요? : P
BarbaraKwarc

0

이것이 필요할 수있는 또 다른 상황은 포인터의 stl 컬렉션이 있고 stl 알고리즘을 사용하여 변경하려는 경우입니다. c ++ 98에서 for_each의 예.

struct Storage {
  typedef std::list<Object*> ObjectList;
  ObjectList objects;

  void change() {
    typedef void (*ChangeFunctionType)(Object*&);
    std::for_each<ObjectList::iterator, ChangeFunctionType>
                 (objects.begin(), objects.end(), &Storage::changeObject);
  }

  static void changeObject(Object*& item) {
    delete item;
    item = 0;
    if (someCondition) item = new Object();
  } 

};

그렇지 않고 changeObject (Object * item) 시그니처를 사용하면 원본이 아닌 포인터의 복사본이 있습니다.


이것은 "개체 변경"보다 "개체 교체"에
가깝
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.