답변:
포인터는 NULL 매개 변수를 수신 할 수 있지만 참조 매개 변수는 수신 할 수 없습니다. "객체 없음"을 전달할 가능성이있는 경우 참조 대신 포인터를 사용하십시오.
또한 포인터로 전달하면 객체가 값 또는 참조로 전달되는지 여부를 호출 사이트에서 명시 적으로 볼 수 있습니다.
// Is mySprite passed by value or by reference? You can't tell
// without looking at the definition of func()
func(mySprite);
// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);
func2 passes by reference
. 코드 수준의 관점에서 포인터를 전달하여 구현 한 높은 수준의 관점에서 "참조로"전달한다는 의미는 있지만 매우 혼란 스러웠습니다 ( stackoverflow.com/questions/13382356/… 참조 ).
func(int& a)
모든 버전의 표준에서 유효한 C가 아닙니다. 우연히 파일을 C ++로 컴파일하고있을 것입니다.
nothing
. 선택적 인수를 제공하는 데 사용할 수 있습니다.string s = &str1 + &str2;
포인터 를 사용할 수 없습니다 .void f(const T& t); ... f(T(a, b, c));
주소를 사용할 수 없으므로 포인터를 사용할 수 없습니다."cplusplus.com :"의 기사에 의한 추론이 마음에 듭니다.
함수가 매개 변수를 수정하지 않고 값을 복사하기 쉬운 경우 (int, double, char, bool 등 ... 단순 유형 std :: string, std :: vector 및 기타 모든 STL 컨테이너는 단순한 유형이 아닙니다.)
값을 복사하는 데 비용이 많이 들고 함수가 AND를 가리키는 값을 수정하지 않으려는 경우 const 포인터로 전달합니다. NULL은 함수가 처리하는 유효한 예상 값입니다.
값이 복사하기에 비싸고 함수가 AND를 가리키는 값을 수정하려고 할 때 상수가 아닌 포인터로 전달합니다. NULL은 함수가 처리하는 유효한 예상 값입니다.
값이 복사하기에 고가이고 함수가 AND를 참조하는 값을 수정하지 않으려는 경우 const 참조로 전달 포인터가 대신 사용 된 경우 NULL은 유효한 값이 아닙니다.
값이 복사하기에 비싸고 함수가 AND를 참조하는 값을 수정하려고 할 때 연속이 아닌 참조로 전달 포인터가 대신 사용 된 경우 NULL은 유효한 값이 아닙니다.
템플릿 함수를 작성할 때이 논의의 범위를 벗어난 것으로 고려해야 할 몇 가지 상충 관계가 있기 때문에 명확한 대답이 없지만 대부분의 템플릿 함수는 값 또는 (const) 참조로 매개 변수를 사용한다고 말할 수 있습니다. 그러나 반복자 구문은 포인터 ( "역 참조"에 대한 별표)와 유사하기 때문에 반복자를 인수로 사용하는 템플릿 함수는 기본적으로 포인터도 허용합니다 (NULL 반복자 개념에 다른 구문이 있으므로 NULL을 확인하지 않음) ).
내가 이것에서 취하는 것은 포인터 또는 참조 매개 변수를 사용하도록 선택하는 것의 주요 차이점은 NULL이 허용 가능한 값인지입니다. 그게 다야.
값이 입력, 출력, 수정 가능 여부 등은 결국 기능에 대한 문서 / 의견에 있어야합니다.
Allen Holub의 "발로 몸을 쏠 수있는 충분한 밧줄"에는 다음 두 가지 규칙이 있습니다.
120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers
그는 C ++에 참조가 추가 된 몇 가지 이유를 나열합니다.
const
참조를 사용하면 복사본을 피하면서 값을 기준으로 의미를 가질 수 있습니다.그의 주요 요점은 호출 사이트에서 매개 변수가 참조인지 또는 값 매개 변수인지에 대한 표시가 없기 때문에 참조를 '출력'매개 변수로 사용해서는 안된다는 것입니다. 따라서 그의 규칙은 const
참조 로만 인수를 사용하는 것입니다.
개인적으로 이것은 매개 변수가 출력 매개 변수인지 아닌지를 명확하게 나타 내기 때문에 이것이 좋은 경험 법칙이라고 생각합니다. 그러나 나는 일반적으로 개인적으로 이것에 동의하지만, 출력 매개 변수를 참조로 주장하는 경우 팀의 다른 사람들의 의견에 흔들릴 수 있습니다 (일부 개발자는 엄청나게 좋아합니다).
이전 게시물에 대한 설명 :
참조는 널이 아닌 포인터를 보장 하지 않습니다 . (우리는 종종 그것들을 그렇게 취급합니다.)
끔찍한 나쁜 코드이지만, 헛된 나쁜 코드의 뒤를 쫓아가는 것처럼 다음은 컴파일 및 실행됩니다 (적어도 내 컴파일러에서는).
bool test( int & a)
{
return (&a) == (int *) NULL;
}
int
main()
{
int * i = (int *)NULL;
cout << ( test(*i) ) << endl;
};
내가 참조로 가지고있는 진짜 문제는 다른 프로그래머 들에게있다. 따라서 생성자에 할당하고 소멸자에 할당 해제 하고 사본 생성자 또는 연산자 = ()를 제공하지 못하는 IDIOTS 라고 불린다 .
갑자기 foo (BAR bar) 와 foo (BAR & bar) 사이에 차이점이 있습니다. (자동 비트 단위 복사 작업이 호출됩니다. 소멸자의 할당 해제가 두 번 호출됩니다.)
고맙게도 현대 컴파일러는 동일한 포인터의 이중 할당 해제를 선택합니다. 15 년 전에는 그렇지 않았습니다. gcc / g ++에서 setenv MALLOC_CHECK_ 0 을 사용 하여 이전 방법을 다시 방문하십시오. DEC UNIX에서 동일한 메모리에서 두 개의 다른 오브젝트에 할당됩니다. 많은 디버깅 재미가 있습니다 ...
더 실질적으로 :
*i
프로그램에는 정의되지 않은 동작이 있습니다. 예를 들어, 컴파일러는이 코드를보고 "OK,이 코드는 모든 코드 경로에서 정의되지 않은 동작을 가지므로이 전체 함수에 도달 할 수 없어야합니다."라고 가정 할 수 있습니다. 그런 다음이 기능으로 이어지는 모든 분기가 수행되지 않는다고 가정합니다. 이것은 정기적으로 수행되는 최적화입니다.
여기의 대부분의 답변은 의도를 표현하는 관점에서 함수 서명에 원시 포인터를 갖는 본질적인 모호성을 해결하지 못합니다. 문제는 다음과 같습니다.
호출자는 포인터가 단일 객체를 가리키는 지 또는 객체의 "배열"의 시작을 가리키는 지 알 수 없습니다.
호출자는 포인터가 가리키는 메모리를 "소유"하는지 알 수 없습니다. IE, 함수가 메모리를 해제해야하는지 여부 ( foo(new int)
-이것이 메모리 누수입니까?).
호출자는 nullptr
함수에 안전하게 전달 될 수 있는지 여부를 모릅니다 .
이러한 모든 문제는 참조로 해결됩니다.
참조는 항상 단일 개체를 나타냅니다.
참조는 참조하는 메모리를 소유하지 않으며 단지 메모리에 대한 관점 일뿐입니다.
참조는 null 일 수 없습니다.
이것은 참조가 일반적인 사용을 위해 훨씬 더 나은 후보가됩니다. 그러나 참조가 완벽하지는 않습니다. 고려해야 할 몇 가지 주요 문제가 있습니다.
&
우리가 실제로 포인터를 전달하고 있음을 나타 내기 위해 연산자를 사용해야하기 때문에 원시 포인터의 문제는 아닙니다 . 예를 들어, int a = 5; foo(a);
여기서 a가 참조로 전달되고 수정 될 수 있다는 것은 명확하지 않습니다.std::optional<T&>
포인터가 우리에게 당신이 원하는 Null 허용을주고, (좋은 이유에 대해) 유효하지 않습니다.따라서 명시 적 간접 지시가있는 nullable 참조를 원할 때 우리는 T*
권리에 도달해야 합니까? 잘못된!
Null 허용에 대한 절망에서 우리는를 위해 도달 할 수 있으며 T*
앞서 나열된 모든 단점과 의미 적 모호성을 무시할 수 있습니다 . 대신, 우리는 C ++이 가장 잘하는 것, 즉 추상화에 도달해야합니다. 포인터를 감싸는 클래스를 작성하면 표현력은 물론 널링 가능성과 명시 적 간접 지시를 얻을 수 있습니다.
template <typename T>
struct optional_ref {
optional_ref() : ptr(nullptr) {}
optional_ref(T* t) : ptr(t) {}
optional_ref(std::nullptr_t) : ptr(nullptr) {}
T& get() const {
return *ptr;
}
explicit operator bool() const {
return bool(ptr);
}
private:
T* ptr;
};
이것은 내가 생각해 낼 수있는 가장 간단한 인터페이스이지만 효과적으로 작동합니다. 참조를 초기화하고 값이 있는지 확인하고 값에 액세스 할 수 있습니다. 다음과 같이 사용할 수 있습니다.
void foo(optional_ref<int> x) {
if (x) {
auto y = x.get();
// use y here
}
}
int x = 5;
foo(&x); // explicit indirection here
foo(nullptr); // nullability
우리는 목표를 달성했습니다! 이제 원시 포인터와 비교하여 이점을 살펴 보겠습니다.
nullptr
함수 작성자가 명시 적으로 요청하기 때문에 호출자는 전달할 수 있음을 알고 있습니다.optional_ref
등식 연산자, 모나 딕 get_or
및 map
인터페이스, 값을 얻거나 예외를 던지는 방법을 추가하는 등의 인터페이스를 여기에서 더 복잡하게 만들 수 constexpr
있습니다. 그것은 당신이 할 수 있습니다.
결론적으로, 원시 포인터를 사용하는 대신 해당 포인터가 실제로 코드에서 의미하는 바에 대해 추론하고 표준 라이브러리 추상화를 활용하거나 직접 작성하십시오. 이렇게하면 코드가 크게 향상됩니다.
실제로는 아닙니다. 내부적으로 참조로 전달은 참조 된 오브젝트의 주소를 본질적으로 전달하여 수행됩니다. 따라서 포인터를 전달하면 효율성이 향상되지 않습니다.
그러나 참조로 전달하면 한 가지 이점이 있습니다. 전달되는 객체 / 유형의 인스턴스가 보장됩니다. 포인터를 전달하면 NULL 포인터를받을 위험이 있습니다. 통과 기준을 사용함으로써 함축적 인 NULL 검사를 함수의 호출자에게 한 레벨 올립니다.
new
포인터와 결과 소유권 문제를 만드는 것을 잊지 마십시오 .