참조 대 포인터를 사용하는 경우


381

포인터와 참조의 구문과 일반적인 의미를 이해하지만 API에서 참조 또는 포인터를 사용하는 것이 적절한 지 어떻게 결정해야합니까?

당연히 일부 상황에는 하나 또는 다른 상황이 필요 operator++하지만 (참조 인수가 필요) 일반적으로 변수가 파괴적으로 전달된다는 구문이 명확하기 때문에 포인터 (및 const 포인터)를 사용하는 것을 선호합니다.

예를 들어 다음 코드에서 :

void add_one(int& n) { n += 1; }
void add_one(int* const n) { *n += 1; }
int main() {
  int a = 0;
  add_one(a); // Not clear that a may be modified
  add_one(&a); // 'a' is clearly being passed destructively
}

포인터를 사용하면 항상 무슨 일이 일어나고 있는지 분명하므로 명확성이 큰 관심사 인 API 등은 참조보다 포인터가 적절하지 않은가? 이것이 참조가 필요할 때만 사용해야한다는 것을 의미합니까 (예 :) operator++? 하나 또는 다른 성능 문제가 있습니까?

수정 된 날짜 :

NULL 값을 허용하고 원시 배열을 처리하는 것 외에도 선택은 개인 취향에 달려 있습니다. Google의 C ++ 스타일 가이드 를 참조하는 아래의 답변을 수락 했습니다. "참조는 값 구문이 있지만 포인터 의미가 있기 때문에 혼동 될 수 있습니다."라는 견해를 제시합니다.

NULL이 아니어야하는 포인터 인수를 삭제하는 데 추가 작업이 필요하기 때문에 (예 : add_one(0)포인터 버전을 호출하고 런타임 중에 중단됨) 유지 보수성 관점에서 객체가 반드시 있어야하는 곳에 참조를 사용하는 것이 부끄러운 일입니다. 구문의 명확성을 잃습니다.


4
언제 사용할 것인지에 대해 이미 결정한 것 같습니다. 개인적으로, 나는 수정하고 있는지 여부에 관계없이 내가하고있는 객체를 전달하는 것을 선호합니다. 함수가 포인터를 가져 가면 포인터에서 작동하는 것, 즉 배열에서 반복자로 사용한다는 것을 알려줍니다.
Benjamin Lindley

1
@ Schnommus : 충분합니다. 주로 TextMate를 사용합니다. 그래도 한 눈에 그 의미가 분명한 것이 바람직하다고 생각합니다.
connec

4
수정 될 add_one(a);것이 확실하지 않은 것은 무엇입니까 a? 그것은 코드에서 바로 말합니다 : add one .
GManNickG

32
@connec : Google C ++ 스타일 가이드는 좋은 C ++ 스타일 가이드로 간주되지 않습니다. Google의 오래된 C ++ 코드 기반으로 작업하기위한 스타일 가이드입니다. 그것을 기반으로 한 대답을 받아들이면 아무도 도움이되지 않습니다. 귀하의 의견과 설명을 읽으면 이미 설정된 의견 으로이 질문에 왔으며 다른 사람들이 귀하의 의견을 확인하기 위해 찾고 있습니다. 결과적으로 당신은 질문을 기초로하고 당신이 듣고 싶어하는 것에 대답합니다.
Martin York

1
이것은 단순히 메소드의 이름을 지정하여 수정됩니다 addOneTo(...). 그것이 당신이하고 싶은 것이 아닌 경우, 선언을보십시오.
스테판

답변:


296

가능한 곳 어디에서나 참조를 사용하십시오.

할 수 없을 때까지 포인터를 피하십시오.

그 이유는 포인터가 다른 구성 요소보다 따르기 어렵고 읽기 어렵고 덜 안전하고 훨씬 더 위험한 조작을하기 때문입니다.

따라서 경험상 다른 선택이없는 경우에만 포인터를 사용하는 것이 좋습니다.

예를 들어, 함수가 nullptr을 반환 할 수있는 경우 객체에 대한 포인터를 반환하는 것이 유효한 옵션이며 원하는 것으로 가정합니다. 즉, 더 나은 옵션은와 비슷한 것을 사용하는 것 boost::optional입니다.

또 다른 예는 특정 메모리 조작을 위해 원시 메모리에 대한 포인터를 사용하는 것입니다. 전체 코드베이스의 위험한 부분을 제한하기 위해 코드의 매우 좁은 부분에 숨겨져 현지화되어야합니다.

귀하의 예에서는 다음과 같은 이유로 포인터를 인수로 사용하는 것이 중요하지 않습니다.

  1. nullptr당신이 논쟁의 여지가 있다면 , 당신은 정의되지 않은 행동의 땅으로 갈 것입니다;
  2. 참조 속성 버전은 1 문제를 허용하지 않습니다 (트릭을 쉽게 발견하지 못함).
  3. 참조 속성 버전은 사용자가 이해하기 더 간단합니다. null이 될 수있는 것이 아니라 유효한 객체를 제공해야합니다.

함수의 동작이 주어진 객체의 유무에 관계없이 작동 해야하는 경우 포인터를 속성 nullptr으로 사용하면 인수로 전달할 수 있으며 함수에 적합하다는 것을 나타냅니다. 그것은 사용자와 구현 사이의 일종의 계약입니다.


49
포인터가 읽기가 더 어렵다고 확신하지 않습니까? 상당히 간단한 개념이며 무언가 수정 될 때 명확 해집니다. 무슨 일이 일어나고 있는지 알 수 없을 때 읽기가 어렵다면 왜 add_one(a)결과를 참조로 설정하지 않고 반환 해서는 안 됩니까?
connec

46
@connec : add_one(a)혼란 스러우면 이름이 잘못 되었기 때문입니다. add_one(&a)똑같은 혼란을 겪을 것입니다. 지금 만 객체가 아닌 포인터를 증가시킬 수 있습니다. add_one_inplace(a)모든 혼란을 피할 것입니다.
Nicol Bolas

20
참고로, 참조는 포인터처럼 쉽게 사라질 수있는 메모리를 가리킬 수 있습니다. 따라서 반드시 포인터보다 안전한 것은 아닙니다. 참조를 유지하고 전달하는 것은 포인터만큼 위험 할 수 있습니다.
Doug T.

6
@Klaim 나는 원시 포인터를 의미했습니다. 나는 C ++에 포인터가 있다는 것을 의미 NULL했으며 nullptr, 이유가 있습니다. 그리고 "포인터를 사용하지 마십시오"및 / 또는 "Null을 사용하지 말고 항상 사용하십시오 boost::optional"라는 사실은 잘 고려되지 않았거나 현실적인 조언이 아닙니다 . 그건 미친 짓이야 나를 잘못 이해하지 마라 .raw 포인터는 C보다 C ++에서 덜 자주 필요하지만 여전히 유용하며 일부 C ++ 사람들이 주장하고 싶어하는 것처럼 "위험하지 않습니다"(과장이기도 함). 포인터를 사용 return nullptr;하고 결 측값을 표시하는 것이 더 쉬운 경우 ... 전체 부스트를 가져 오는 이유는 무엇입니까?

5
@Klaim "Null을 사용하는 것은 좋지 않습니다"-이제 말도 안됩니다. 그리고 if쓸모없고 while() { break; }대신 사용해야 합니까? 또한 걱정하지 마십시오. 큰 코드 기반을보고 작업했습니다. 예, 부주의 한 경우 소유권이 문제입니다. 규칙에 충실하지 않더라도 규칙을 일관되게 사용하고 코드에 주석을 달고 문서화하십시오. 그러나 결국 C ++에는 너무 바보이기 때문에 C를 사용해야합니다.

62

참조는 내부적으로 포인터로 구현되므로 성능은 동일합니다. 따라서 걱정할 필요가 없습니다.

참조 및 포인터 사용시기와 관련하여 일반적으로 허용되는 규칙은 없습니다. 경우에 따라 참조 (예 : 복사 생성자)를 반환하거나 수락해야하지만 원하는 경우를 제외하고 자유롭게 할 수 있습니다. 필자가 만난 일반적인 규칙은 매개 변수가 기존 객체를 참조해야 할 때 참조를 사용하고 NULL 값이 괜찮을 때 포인터를 사용하는 것입니다.

Google 과 같은 일부 코딩 규칙 은 참조가 구문이 명확하지 않기 때문에 항상 포인터 또는 const 참조를 사용해야한다고 규정합니다.


10
이것에 조금만 더 추가하기 위해 Google의 스타일 가이드에 따르면 함수에 대한 입력 매개 변수는 const 참조 여야하고 출력은 포인터 여야한다고합니다. 함수 서명을 읽을 때 입력과 출력이 무엇인지 명확하게 알 수 있기 때문에 이것을 좋아합니다.
Dan

44
@Dan : Google 스타일 가이드는 Google의 이전 코드를위한 것으로 최신 코딩에는 사용해서는 안됩니다. 사실, 그것은 새로운 프로젝트에 있어 다소 나쁜 코딩 스타일입니다.
GManNickG

13
@connec : 이런 식으로하자 : null은 완벽하게 유효한 포인터 값이다. 포인터가있는 곳이면 null 값을 줄 수 있습니다. Ergo의 두 번째 버전 add_one깨졌습니다 : add_one(0); // passing a perfectly valid pointer value, kaboom. null인지 확인해야합니다. 어떤 사람들은 반박 할 것입니다. "글쎄, 내 함수가 null로 작동하지 않는다는 것을 문서화 할 것입니다". 괜찮습니다.하지만 질문의 목적을 무너 립니다 .null이 괜찮은지 확인하기 위해 설명서를 보려는 경우 함수 선언도 표시 됩니다.
GManNickG

8
그것이 참조라면 그 경우가 될 것입니다. 그러나 이러한 레토르트는 요점을 놓친다. 참조 는 언어 수준 에서 기존 객체를 참조 하는 언어 수준에서 시행 되며 , 포인터는 그러한 제한이 없다. 언어 수준의 시행이 문서 수준의 집행보다 강력하고 오류 발생이 적은 것이 분명하다고 생각합니다. "봐, 널 (null) 참조 : 어떤 말을하여이에 레토르트 위해 노력할 것 int& i = *((int*)0);이 유효한 레토르트 없습니다.. 이 문제를 이전 코드 거짓말에 포인터를 이용하여이 아닌 참조하여 참고 문헌이 기간 널 결코..
GManNickG

12
안녕하세요, 의견에 언어 변호사가 부족하다는 것을 알았으므로 치료해 드리겠습니다. 참조는 일반적 으로 포인터로 구현되지만 표준은 그러한 것을 말하지 않습니다. 다른 메커니즘을 사용한 구현은 100 % 불만입니다.
토마스 보니 니

34

에서 C ++ FAQ 라이트 -

가능하면 참조를 사용하고 필요할 때는 포인터를 사용하십시오.

참조는 "재 부착"이 필요하지 않을 때마다 포인터보다 선호됩니다. 이것은 일반적으로 참조가 클래스의 공용 인터페이스에서 가장 유용하다는 것을 의미합니다. 참조는 일반적으로 개체의 스킨에 나타나고 내부에 포인터가 나타납니다.

위의 예외는 함수의 매개 변수 또는 반환 값에 "센티넬"참조 (객체를 참조하지 않는 참조)가 필요한 경우입니다. 이것은 일반적으로 포인터를 반환 / 가져 와서 NULL 포인터 에이 특별한 의미를 부여하는 것이 가장 좋습니다 (참조는 항상 참조 해제 된 NULL 포인터가 아닌 객체의 별칭을 지정해야 함).

참고 : 구식 C 프로그래머는 호출자의 코드에 명시되지 않은 참조 시맨틱을 제공하기 때문에 참조를 좋아하지 않는 경우가 있습니다. 그러나 일부 C ++ 경험을 한 후에는 이것이 정보 숨기기의 형태라는 것을 빨리 인식하고, 이는 책임이 아닌 자산입니다. 예를 들어, 프로그래머는 기계 언어가 아닌 문제의 언어로 코드를 작성해야합니다.


1
API를 사용하는 경우 API에 대해 잘 알고 전달 된 매개 변수가 수정되었는지 여부를 알아야한다고 생각할 수도 있지만 C 프로그래머와 동의한다고 생각합니다. C 경험이 거의 없지만). 더 명확한 구문은 프로그래머뿐만 아니라 기계에게도 도움이됩니다.
connec

1
@connec : C 프로그래머가 자신의 언어에 맞는지 확인하십시오. 그러나 C ++를 C로 취급하는 실수를하지 마십시오. 완전히 다른 언어입니다. C ++를 C로 취급하면 C with class(C ++이 아닌) 동일하게 참조되는 것을 작성하게됩니다.
Martin York

15

내 경험 법칙은 다음과 같습니다.

  • 나가거나 들어가거나 나가는 매개 변수에 포인터를 사용하십시오. 따라서 값이 변경 될 것임을 알 수 있습니다. (을 사용해야합니다 &)
  • NULL 매개 변수가 허용 가능한 값이면 포인터를 사용하십시오. ( const수신 매개 변수 인지 확인하십시오 )
  • NULL이 될 수없고 기본 유형 ( const T&) 이 아닌 경우 수신 매개 변수에 대한 참조를 사용하십시오 .
  • 새로 만든 객체를 반환 할 때 포인터 또는 스마트 포인터를 사용하십시오.
  • 포인터 대신 포인터 나 스마트 포인터를 구조체 나 클래스 멤버로 사용하십시오.
  • 앨리어싱 사용 참조 (예. int &current = someArray[i])

어떤 것을 사용하든, 명확하지 않은 경우 함수와 매개 변수의 의미를 문서화하는 것을 잊지 마십시오.


14

면책 조항 : 참조가 NULL 또는 "리바운드"가 될 수 없다는 사실 이외 (타이어는 별명 인 객체를 변경할 수 없음을 의미 함) 실제로 맛의 문제로 귀결됩니다. "이게 낫다".

즉, 게시물의 마지막 진술에 동의하지 않습니다. 코드가 참조로 명확성을 잃지 않는다고 생각합니다. 귀하의 예에서

add_one(&a);

보다 명확하다

add_one(a);

왜냐하면 아마도 a의 가치가 변할 것이라는 것을 알고 있기 때문입니다. 반면에 함수의 서명

void add_one(int* const n);

n은 단일 정수 또는 배열입니까? 때로는 (잘 문서화되지 않은) 헤더 및 다음과 같은 서명에만 액세스 할 수 있습니다

foo(int* const a, int b);

첫눈에 이해하기 쉽지 않습니다.

Imho, 참조는 (재 설명 된 의미에서) (재) 할당이나 리 바인딩이 필요하지 않을 때 포인터만큼 좋습니다. 또한 개발자가 배열에 대한 포인터 만 사용하는 경우 함수 시그니처는 다소 모호합니다. 연산자 구문이 참조로 더 읽기 쉽다는 사실은 말할 것도 없습니다.


두 솔루션 모두 명확성을 확보하고 잃는 곳을 명확하게 보여 주셔서 감사합니다. 나는 처음에 포인터 캠프에 있었지만 이것은 많은 의미가 있습니다.
Zach Beavon-Collin

12

다른 사람들이 이미 대답했듯이 : NULL/ 변수 nullptr실제로 유효한 상태 가 아닌 한 항상 참조를 사용하십시오 .

이 주제에 대한 John Carmack의 견해는 비슷합니다.

NULL 포인터는 적어도 코드에서 C / C ++에서 가장 큰 문제입니다. 단일 값을 플래그와 주소로 이중 사용하면 치명적인 문제가 발생합니다. C ++ 참조는 가능할 때마다 포인터보다 선호되어야합니다. 참조는 "진정한"포인터 일 뿐이며, NULL이 아닌 암시 적 계약을 가지고 있습니다. 포인터가 참조로 바뀔 때 NULL 검사를 수행하면 그 이후의 문제를 무시할 수 있습니다.

http://www.altdevblogaday.com/2011/12/24/static-code-analysis/

2012-03-13 수정

Bret Kuhns 사용자 는 다음과 같이 말합니다.

C ++ 11 표준이 완성되었습니다. 이 스레드에서 대부분의 코드가 참조, shared_ptr 및 unique_ptr의 조합으로 완벽하게 잘 수행되어야한다고 언급 할 때가되었습니다.

충분히 사실이지만 원시 포인터를 스마트 포인터로 바꾸더라도 여전히 문제는 남아 있습니다.

예를 들어, 모두 std::unique_ptrstd::shared_ptr기본 생성자를 통해 "빈"포인터로 구성 할 수있다 :

... 비어 있지 않은지 확인하지 않고 사용하면 충돌이 발생할 위험이 있습니다. 이것은 J. Carmack의 토론과 관련이 있습니다.

그리고 "우리는 어떻게 스마트 포인터를 함수 매개 변수로 전달합니까?"라는 재미있는 문제가 있습니다.

질문 C ++-boost :: shared_ptr에 대한 참조 전달에 대한 Jon대답 에 따르면 다음 의견은 스마트 포인터를 복사 또는 참조로 전달하는 것이 원하는만큼 명확하지 않다는 것을 보여줍니다 ( by-reference "로 기본 설정되어 있지만 잘못되었을 수 있습니다).


1
C ++ 11 표준이 완성되었습니다. 이 스레드에서 대부분의 코드가 참조 shared_ptr, 및을 조합하여 완벽하게 잘 수행해야한다고 언급 할 때가되었습니다 unique_ptr. 소유권 의미 및 입 / 출력 매개 변수 규칙은이 세 가지 요소와 구성 요소의 조합으로 처리됩니다. 레거시 코드 및 매우 최적화 된 알고리즘을 처리 할 때를 제외하고 C ++에서는 원시 포인터가 거의 필요하지 않습니다. 그것들이 사용되는 영역은 가능한 한 캡슐화되어야하고 원시 포인터를 의미 적으로 적절한 "현대적인"동등한 것으로 변환해야합니다.
Bret Kuhns

1
많은 시간 동안 스마트 포인터를 전달해서는 안되지만 null을 테스트 한 다음 포함 된 개체를 참조로 전달해야합니다. 실제로 스마트 포인터를 전달해야하는 유일한 시간은 다른 객체와 소유권을 전송 (unique_ptr)하거나 공유 (shared_ptr)하는 경우입니다.
누가 가치

@povman : 전적으로 동의합니다 : 소유권이 인터페이스의 일부가 아닌 경우 (그리고 수정하지 않는 한), 스마트 포인터를 매개 변수 (또는 반환 값)로 전달해서는 안됩니다. 소유권이 인터페이스의 일부인 경우 상황이 조금 더 복잡해집니다. 예를 들어 Sutter / Meyers는 unique_ptr을 매개 변수로 전달하는 방법에 대해 토론합니다 (복사 (Sutter) 또는 r- 값 참조 (Meyers))? 안티 패턴은 포인터가 무효화 될 위험이있는 글로벌 shared_ptr에 포인터를 전달하는 것에 의존합니다 (솔루션이 스택에 스마트 포인터를 복사하는 중)
paercebal

7

맛의 문제가 아닙니다. 다음은 몇 가지 결정적인 규칙입니다.

선언 된 범위 내에서 정적으로 선언 된 변수를 참조하려면 C ++ 참조를 사용하면 완벽하게 안전합니다. 정적으로 선언 된 스마트 포인터에도 동일하게 적용됩니다. 매개 변수를 참조로 전달하는 것이이 사용법의 예입니다.

범위가 선언 된 범위보다 넓은 범위에서 무언가를 참조하려면 참조 카운트 스마트 포인터를 사용하여 완벽하게 안전해야합니다.

구문상의 편의를 위해 참조가있는 컬렉션 요소를 참조 할 수 있지만 안전하지는 않습니다. 언제든지 요소를 삭제할 수 있습니다.

컬렉션의 요소에 대한 참조를 안전하게 유지하려면 참조 횟수 스마트 포인터를 사용해야합니다.


5

성능 차이는 너무 작아서 덜 분명한 접근 방식을 사용하여 정당화 할 수는 없습니다.

첫째, 참고 문헌이 일반적으로 우수한 경우에 언급되지 않은 사례는 const참고 문헌입니다. 단순하지 않은 유형의 경우, 전달 const reference하면 임시 작성을 피할 수 있으며 값이 수정되지 않기 때문에 우려되는 혼동을 일으키지 않습니다. 여기에서 사람이 포인터를 전달하도록 강요하면 주소가 가져오고 함수에 전달되는 것을 볼 때 값이 변경되었다고 생각할 수 있으므로 걱정이 심합니다.

어쨌든, 나는 기본적으로 당신에 동의합니다. 나는 이것이 함수가하는 일이 확실하지 않을 때 값을 수정하기 위해 참조를 취하는 함수를 좋아하지 않습니다. 이 경우 포인터를 사용하는 것이 좋습니다.

복잡한 유형의 값을 반환해야 할 때 참조를 선호하는 경향이 있습니다. 예를 들면 다음과 같습니다.

bool GetFooArray(array &foo); // my preference
bool GetFooArray(array *foo); // alternative

여기서 함수 이름을 사용하면 배열로 정보를 다시 가져올 수 있습니다. 따라서 혼란이 없습니다.

참조의 주요 장점은 항상 유효한 값을 포함하고 포인터보다 깨끗하며 추가 구문 없이도 다형성을 지원한다는 것입니다. 이러한 이점 중 어느 것도 적용되지 않으면 포인터보다 참조를 선호 할 이유가 없습니다.


4

위키 에서 복사 -

그 결과 많은 구현에서 참조를 통해 자동 또는 정적 수명을 갖는 변수에서 작동하는 것은 구문 적으로 직접 액세스하는 것과 유사하지만 비용이 많이 드는 숨겨진 역 참조 작업을 포함 할 수 있습니다. 참조는 식별자의 간접적 인 수준을 모호하게하기 때문에 구문 상 논란의 여지가있는 C ++ 기능입니다. 즉, 포인터가 일반적으로 구문 상 두드러지는 C 코드와 달리, 큰 C ++ 코드 블록에서 액세스중인 객체가 로컬 또는 전역 변수로 정의되어 있는지 또는 참조 (암시 적 포인터)인지 여부는 즉시 명확하지 않을 수 있습니다. 다른 위치, 특히 코드가 참조와 포인터를 혼합하는 경우. 이 측면은 잘못 작성된 C ++ 코드를 읽고 디버그하기 어렵게 만들 수 있습니다 (별칭 참조).

나는 이것에 100 % 동의하고, 이것이 당신이 그렇게 할만한 이유가있을 때만 참조를 사용해야한다고 믿는 이유입니다.


나는 또한 상당히 동의하지만, NULL 포인터에 대한 내장 보호의 손실은 순전히 구문 론적 인 문제에 대해 너무 비용이 많이 든다는 견해로 다가오고 있습니다. 어쨌든.
connec

상황도 중요한 요소라고 생각합니다. 현재 코드베이스가 주로 포인터를 사용할 때 참조를 사용하는 것은 나쁜 생각이라고 생각합니다. 당신이 그들의 참조를 기대한다면 그들의 암시적인 의미가 덜 중요하다는 사실은 ..
user606723

3

명심해야 할 사항 :

  1. 포인터는 NULL참조가 될 수 없습니다 NULL.

  2. 참조는 사용하기 쉽고 const값을 변경하고 싶지 않고 함수에서 참조가 필요할 때 참조로 사용할 수 있습니다.

  3. 와 함께 사용되는 *while 참조 와 함께 사용되는 포인터 &.

  4. 포인터 산술 연산이 필요한 경우 포인터를 사용하십시오.

  5. void 유형에 대한 포인터는 가질 수 int a=5; void *p = &a;있지만 void 유형에 대한 참조는 가질 수 없습니다.

포인터 대 참조

void fun(int *a)
{
    cout<<a<<'\n'; // address of a = 0x7fff79f83eac
    cout<<*a<<'\n'; // value at a = 5
    cout<<a+1<<'\n'; // address of a increment by 4 bytes(int) = 0x7fff79f83eb0
    cout<<*(a+1)<<'\n'; // value here is by default = 0
}
void fun(int &a)
{
    cout<<a<<'\n'; // reference of original a passed a = 5
}
int a=5;
fun(&a);
fun(a);

무엇을 사용해야하는지 평결

포인터 : 배열, 링크 목록, 트리 구현 및 포인터 산술.

참조 : 함수 매개 변수 및 리턴 유형


2

" 가능한 경우 참조 사용 "규칙에 문제점이 있으며 추가 사용을 위해 참조를 유지하려는 경우 발생합니다. 예를 들어 이것을 설명하기 위해 다음과 같은 수업이 있다고 상상해보십시오.

class SimCard
{
    public:
        explicit SimCard(int id):
            m_id(id)
        {
        }

        int getId() const
        {
            return m_id;
        }

    private:
        int m_id;
};

class RefPhone
{
    public:
        explicit RefPhone(const SimCard & card):
            m_card(card)
        {
        }

        int getSimId()
        {
            return m_card.getId();
        }

    private:
        const SimCard & m_card;
};

처음에는 RefPhone(const SimCard & card)생성자에 잘못된 / 널 포인터를 전달하지 못하므로 생성자에 매개 변수 가 참조에 의해 전달되는 것이 좋습니다 . 어떻게 든 스택에 변수를 할당하고 RAII의 이점을 얻습니다.

PtrPhone nullPhone(0);  //this will not happen that easily
SimCard * cardPtr = new SimCard(666);  //evil pointer
delete cardPtr;  //muahaha
PtrPhone uninitPhone(cardPtr);  //this will not happen that easily

그러나 임시 사람들은 당신의 행복한 세상을 파괴하기 위해옵니다.

RefPhone tempPhone(SimCard(666));   //evil temporary
//function referring to destroyed object
tempPhone.getSimId();    //this can happen

따라서 맹목적으로 참조를 고수하면 파괴 된 객체에 대한 참조를 저장할 가능성에 대해 유효하지 않은 포인터를 전달할 가능성과 상충되며 이는 기본적으로 동일한 효과입니다.

편집 : 규칙 "나는 당신이 할 수있는 곳마다 포인터를 사용하십시오. 당신이 할 수있을 때까지 포인터를 피하십시오." 가장 많이지지되고 승인 된 답변 (다른 답변도 제안)에서 명백해야하지만, 예는 그러한 참조가 나쁘다는 것을 보여주지 않습니다. 그러나 포인터처럼 오용 될 수 있으며 코드에 자체 위협을 가져올 수 있습니다.


포인터와 참조에는 다음과 같은 차이점이 있습니다.

  1. 변수를 전달할 때 참조로 전달은 값으로 전달하는 것처럼 보이지만 포인터 의미가 있습니다 (포인터처럼 작동).
  2. 참조는 0으로 직접 초기화 할 수 없습니다 (널).
  3. 참조 (참조, 참조되지 않은 객체)는 수정할 수 없습니다 ( "* const"포인터와 동일).
  4. const 참조는 임시 매개 변수를 승인 할 수 있습니다.
  5. 임시 객체의 수명을 연장시키는 로컬 const 참조

이 규칙을 고려하여 현재 규칙은 다음과 같습니다.

  • 함수 범위 내에서 로컬로 사용될 매개 변수에 대한 참조를 사용하십시오.
  • 0 (널)이 허용 가능한 매개 변수 값이거나 추가 사용을 위해 매개 변수를 저장해야하는 경우 포인터를 사용하십시오. 0 (널)이 허용되는 경우 매개 변수에 "_n"접미사를 추가하고 보호 포인터 (Qt의 QPointer와 같은)를 사용하거나 문서화하십시오. 스마트 포인터를 사용할 수도 있습니다. 일반적인 포인터보다 공유 포인터에 더주의를 기울여야합니다 (그렇지 않으면 디자인 메모리 누수와 책임 혼란으로 끝날 수 있습니다).

3
예제의 문제점은 참조가 안전하지 않다는 것이 아니라 프라이빗 멤버를 유지하기 위해 객체 인스턴스의 범위를 벗어난 것에 의존한다는 것입니다. const SimCard & m_card;잘못 작성된 코드 일뿐입니다.
plamenko

@ plamenko 나는 당신이 예제의 목적을 이해하지 못하는 것이 두렵습니다. const SimCard & m_card올바른지 여부 는 상황에 따라 다릅니다. 이 게시물의 메시지는 참조가 안전하지 않다는 것이 아닙니다 (열심히 시도해도 될 수 있음). 메시지는 "가능할 때마다 참조를 사용하는"진언을 맹목적으로 고수해서는 안된다는 것입니다. 예는 "가능할 때마다 참조 사용"교리를 적극적으로 사용한 결과입니다. 이것은 분명해야합니다.
doc

그 문제에 대해 더 많이 배우려고 노력하는 사람을 오도 할 수 있다고 생각하기 때문에 귀하의 답변에 귀찮게하는 두 가지가 있습니다. 1. 게시물은 단방향이며 참조가 잘못되었다는 인상을 받기 쉽습니다. 참조를 사용하지 않는 방법에 대한 단일 예제 만 제공했습니다. 2. 당신은 당신의 예에서 무엇이 잘못되었는지 명확하지 않았습니다. 그렇습니다. 임시는 소멸 될 것이지만, 잘못된 행은 아니 었습니다. 그것은 클래스의 구현입니다.
plamenko

실제로 같은 회원이 없어야합니다 const SimCard & m_card. 임시 작업을 효율적으로 수행하려면 explicit RefPhone(const SimCard&& card)생성자를 추가하십시오 .
plamenko

@ plamenko 기본 이해력을 읽을 수 없다면 내 게시물에 오도되는 것보다 더 큰 문제가 있습니다. 내가 어떻게 더 명확해질 수 있는지 모르겠습니다. 첫 문장을보세요. "가능할 때마다 참조 사용"만트라에 문제가 있습니다! 내 게시물에서 참조가 잘못되었다는 진술을 어디에서 찾았습니까? 내 게시물의 끝 부분에서 참조를 사용할 위치를 작성 했으므로 그러한 결론을 어떻게 얻었습니까? 이것은 질문에 대한 직접적인 대답이 아닙니까?
doc

1

다음은 몇 가지 지침입니다.

함수는 전달 된 데이터를 수정하지 않고 사용합니다.

  1. 내장 데이터 형식이나 작은 구조와 같이 데이터 개체가 작 으면 값으로 전달하십시오.

  2. 데이터 객체가 배열 인 경우 유일한 선택이기 때문에 포인터를 사용하십시오. 포인터를 const에 대한 포인터로 만듭니다.

  3. 데이터 객체의 크기가 적당한 구조라면 const 포인터 나 const 참조를 사용하여 프로그램 효율성을 높이고 구조 나 클래스 디자인을 복사하는 데 필요한 시간과 공간을 절약하십시오. 포인터 또는 참조를 const로 만드십시오.

  4. 데이터 객체가 클래스 객체 인 경우 const 참조를 사용합니다. 클래스 디자인의 시맨틱은 종종 참조를 사용해야하는데, 이는 C ++이이 기능을 추가 한 주된 이유이므로 클래스 객체 인수를 전달하는 표준 방법은 참조입니다.

함수는 호출 함수의 데이터를 수정합니다.

1. 데이터 개체가 내장 데이터 형식 인 경우 포인터를 사용하십시오. x가 int 인 fixit (& x)와 같은 코드를 발견하면이 함수가 x를 수정하려고한다는 것이 분명합니다.

2. 데이터 객체가 배열이면 포인터 만 선택하십시오.

3. 데이터 객체가 구조 인 경우 참조 또는 포인터를 사용하십시오.

데이터 개체가 클래스 개체 인 경우 참조를 사용하십시오.

물론 이것은 지침 일 뿐이며 다른 선택을해야하는 이유가있을 수 있습니다. 예를 들어, cin은 기본 유형에 대한 참조를 사용하므로 cin >> & n 대신 cin >> n을 사용할 수 있습니다.


0

참조는 더 깨끗하고 사용하기 쉬우 며 정보를 숨기는 것이 더 좋습니다. 그러나 참조는 재 할당 할 수 없습니다. 먼저 한 객체를 가리킨 다음 다른 객체를 가리켜 야하는 경우 포인터를 사용해야합니다. 참조는 null 일 수 없으므로 문제의 개체가 null 일 가능성이있는 경우 참조를 사용해서는 안됩니다. 포인터를 사용해야합니다. 객체 조작을 직접 처리하려면, 즉 스택 대신 힙의 객체에 메모리 공간을 할당하려면 포인터를 사용해야합니다

int *pInt = new int; // allocates *pInt on the Heap

0

올바르게 작성된 예제는 다음과 같아야합니다.

void add_one(int& n) { n += 1; }
void add_one(int* const n)
{
  if (n)
    *n += 1;
}

그렇기 때문에 가능한 경우 참조가 바람직합니다 ...


-1

그냥 내 입소문을 넣어. 난 그냥 테스트를 수행했습니다. 그거 참 재밌 네요 g ++은 참조를 사용하는 것과 비교하여 포인터를 사용하여 동일한 미니 프로그램의 어셈블리 파일을 만들도록했습니다. 출력을 볼 때 정확히 동일합니다. 심볼 명 이외의 것. 따라서 간단한 예제에서 성능을 살펴보면 아무런 문제가 없습니다.

이제 포인터 대 참조 주제에 대해 IMHO 나는 명확성이 무엇보다 중요하다고 생각합니다. 암시 적 동작을 읽 자마자 발가락이 말리기 시작합니다. 참조가 NULL이 될 수 없다는 것은 암묵적인 동작이라는 것에 동의합니다.

NULL 포인터를 역 참조하는 것은 문제가되지 않습니다. 응용 프로그램이 중단되고 쉽게 디버깅 할 수 있습니다. 더 큰 문제는 유효하지 않은 값을 포함하는 초기화되지 않은 포인터입니다. 이것은 명백한 출처없이 정의되지 않은 동작을 일으키는 메모리 손상을 일으킬 가능성이 높습니다.

이것은 참조가 포인터보다 훨씬 안전하다고 생각하는 곳입니다. 그리고 나는 인터페이스 (명확하게 문서화되어야하고, 계약에 의한 디자인 참조, Bertrand Meyer)가 매개 변수의 결과를 함수에 정의한다는 이전 진술에 동의합니다. 이제이 모든 것을 고려하여 내 환경 설정은 가능한 한 언제 어디서나 참조를 사용합니다.


-2

포인터의 경우 무언가를 가리켜 야하므로 포인터에 메모리 공간이 필요합니다.

예를 들어 정수 포인터를 사용하는 함수는 정수 변수를 사용하지 않습니다. 따라서 먼저 함수에 전달할 포인터를 작성해야합니다.

참고로 메모리 비용은 들지 않습니다. 정수 변수가 있으며이를 참조 변수로 전달할 수 있습니다. 그게 다야. 이를 위해 특별히 참조 변수를 만들 필요는 없습니다.


4
아니. 포인터를 취하는 함수에는 포인터 변수를 할당 할 필요가 없습니다 &address. 임시를 전달할 수 있습니다 . 참조는 확실히 객체의 멤버 인 경우 메모리 비용이 들며 모든 기존 컴파일러는 실제로 참조를 주소로 구현하므로 매개 변수 전달 또는 역 참조 측면에서 아무것도 저장하지 않습니다.
underscore_d
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.