참조 (C++)
Reference (C++)C++ 프로그래밍 언어에서 참조는 C에서 상속된 포인터 유형보다 성능은 떨어지지만 안전한 단순 참조 데이터 유형입니다.C++ 참조라는 이름은 컴퓨터 과학에서 참조는 일반적인 개념의 데이터 유형이며 포인터와 C++ 참조는 특정 참조 데이터 유형 구현입니다.C++에서 참조의 정의는 존재하지 않아도 되는 것입니다.기존 개체의 새 이름으로 구현할 수 있습니다(Ada의 rename 키워드와 유사).
구문 및 용어
양식의 선언:
<유형>& <이름>
어디에<Type>
유형이고.<Name>
는 식별자는 유형이 lvalue 참조인 식별자를 정의한다고 합니다.[1]
예:
인트 a = 5; 인트& r_a = a; 외전의 인트& r_b;
여기서,r_a
그리고.r_b
유형 "l 값 참조int
"
인트& 푸();
Foo
는 "l 값 참조를 반환하는 함수입니다.int
"
공허한 바(인트& r_p);
Bar
는 기준 매개변수를 갖는 함수이며, 이는 "l 값 참조:int
"
학급 마이클래스 { 인트& m_b; /* ... */ };
MyClass
가class
에 대해 가치있는 언급을 하는 멤버와 함께.int
인트 FuncX() { 돌아가다 42 ; }; 인트 (&f_func)() = FuncX;
FuncX
는 (비참조 유형) 을 반환하는 함수입니다.int
그리고.f_func
는 에 대한 별칭입니다.FuncX
머리를 짜다 인트& ref = 65;
const int& ref
는 다음에 대한 l 값 참조입니다.const int
가치가 65인 저장소를 가리키고 있습니다.
양식의 선언:
<타입>&&<이름>
어디에<Type>
유형이고.<Name>
는 타입이 rvalue reference인 식별자를 정의한다고 합니다. rvalue reference의 이름은 그 자체가 alvalue이므로,std::move
rvalue 참조 매개변수를 허용하는 함수 오버로드에 rvalue 참조를 전달하는 데 사용해야 합니다.동일한 함수 템플릿의 cv-unqualified type 템플릿 매개 변수에 대한 Rvalue 참조 또는auto&&
브레이스-슬라이스 이니셜라이저 목록에서 추론한 경우를 제외하고는 전달 참조(일부 오래된[2] 소스에서는 "보편 참조"로 표시됨)라고 하며 전달되는 [3]내용에 따라 lvalue 참조 또는 rvalue 참조로 작동할 수 있습니다.함수 매개 변수에서 찾을 수 있는 경우 때때로 다음과 함께 사용됩니다.std::forward
호출 함수로 전달될 때 [4]값 범주(lvalue 또는 rvalue)를 보존하면서 함수 인수를 다른 함수로 전달합니다.
일종의 "참조"에 해당하는 유형<Type>
" 참조 유형이라고도 합니다.참조 유형의 식별자를 참조 변수라고 합니다.그러나 변수라고 부르는 것은 앞으로 보게 되겠지만 사실 잘못된 이름입니다.
참조 배열, 참조에 대한 포인터 및 참조에 대한 참조는 허용되지 않습니다.int& i[4]
,int&*i
그리고.int& &i
컴파일 오류가 발생합니다.int(& i)[4]
그리고.int*&i
초기화되었다고 가정하지 않을 것입니다).참고문헌void
또한 모양이 좋지 않습니다.참조를 상수 또는 휘발성()으로 선언합니다.int& volatile i
) 또한 const/volatile이 무시되는 typeef/volatiletype을 사용하지 않는 한 실패합니다.그러나 템플릿 인수 차감이 발생하고 참조 유형이 추론되는 경우(참조 참조가 사용되고 함수에 값이 전달될 때 발생함) 또는typedef
,using
아니면decltype
참조 유형을 나타냅니다. 해당 유형에 대한 참조를 취할 수 있습니다.이 경우 참조 유형을 결정하는 데 사용되는 규칙을 참조 접기라고 하며 다음과 같이 작동합니다.유형 가정T
참조 유형은 다음과 같습니다.T
TR
, r값 참조 만들기 시도TR
를 만듭니다.TR
를 참조하는 동안TR
다음 값 참조를 생성합니다.T
. 즉, l값 참조 재정의 값 참조와 r값 참조의 r값 참조는 변경되지 않습니다.
인트 i; 타이프 디프 인트& LRI; 사용. RRI = 인트&&; LRI& r1 = i; // r1은 int&형입니다. 머리를 짜다 LRI& r2 = i; // r2는 int&형입니다. 머리를 짜다 LRI&& r3 = i; // r3는 int&형입니다. RRI& r4 = i; // r4는 int&형입니다. RRI&& r5 = 5; // r5는 int& 타입을 가지고 있습니다. decl타입(r2)& r6 = i; // r6는 int&형입니다. decl타입(r2)&& r7 = i; // r7은 int&형입니다.
고정적이지 않은 멤버 함수는 ref 한정자를 사용하여 선언할 수 있습니다.이 한정자는 오버로드 해결에 참여하며 다음과 같은 암시적 개체 매개 변수에 적용됩니다.const
그리고.volatile
하지만 그 둘과 달리, 그것은 그들의 속성을 바꾸지 않습니다.this
. 이것이 하는 일은 클래스의 lvalue 또는 rvalue 인스턴스에서 함수를 호출하도록 명령하는 것입니다.
#허튼소리 <아이오스트림> 짜임새 있는 A { A() = 체납; 공허한 프린트()머리를 짜다& { std::꾸트린 << "l value\n"; } 공허한 프린트()머리를 짜다&& { std::꾸트린 << "rvalue\n"; } }; 인트 주된() { A a; a.프린트(); // 인쇄 "lvalue" std::움직이다(a).프린트(); // 인쇄 "rvalue" A().프린트(); // 인쇄 "rvalue" A&& b = std::움직이다(a); b.프린트(); // "lvalue"(!)를 인쇄합니다. }
포인터와의 관계
C++ 참조는 몇 가지 필수적인 점에서 포인터와 다릅니다.
- 참조 개체가 정의된 후에는 참조 개체를 직접 참조할 수 없습니다. 이름이 발생하면 참조 개체를 직접 참조합니다.
- 참조가 생성되면 나중에 다른 개체를 참조할 수 없으며 다시 설치할 수 없습니다.이것은 종종 포인터로 이루어집니다.
- 참조는 null일 수 없지만 포인터는 null일 수 있습니다. 모든 참조는 유효할 수도 있고 그렇지 않을 수도 있지만 일부 개체를 참조합니다.이러한 이유로 참조의 용기는 허용되지 않습니다.
- 참조를 초기화 취소할 수 없습니다.참조를 다시 초기화하는 것은 불가능하기 때문에, 참조를 생성하는 즉시 초기화해야 합니다.특히 로컬 및 글로벌 변수는 정의된 위치에서 초기화되어야 하며 클래스 생성자의 초기화 목록에서 클래스 인스턴스의 데이터 멤버인 참조를 초기화해야 합니다.예를 들어,
인트& k; // 컴파일러가 불만을 제기합니다. 오류: 'k'가 참조로 선언되었지만 초기화되지 않았습니다.
포인터와 참조 사이에 간단한 변환이 있습니다: 주소 연산자 (&
)은 참조에 적용될 때 동일한 객체를 가리키는 포인터를 생성하고 참조 해제에서 초기화된 참조 (*
포인터 값의 )은 해당 포인터와 동일한 개체를 가리킵니다. 여기서 정의되지 않은 동작을 호출하지 않고도 가능합니다.이 동등성은 일반적인 구현을 반영한 것으로, 참조를 효과적으로 사용할 때마다 암시적으로 역참조되는 포인터로 컴파일합니다.일반적으로 그러하지만 C++ 표준에서는 컴파일러가 포인터를 사용하여 참조를 구현하도록 강제하지 않습니다.
이로 인해 많은 구현에서 참조를 통해 자동 또는 정적 수명을 가진 변수에서 작동하는 것은 구문적으로는 직접 액세스하는 것과 비슷하지만 비용이 많이 드는 숨겨진 참조 제거 작업을 수반할 수 있습니다.
또한 참조에 대한 연산이 매우 제한적이기 때문에 포인터보다 훨씬 이해하기 쉽고 오류에 더 저항력이 있습니다.포인터는 널 값을 전달하는 것부터 경계 밖 산술, 불법 캐스트, 임의 정수에서 생성하는 것까지 다양한 메커니즘을 통해 무효로 만들 수 있지만 이전에 유효했던 참조는 두 가지 경우에만 무효가 됩니다.
- 범위를 벗어난 자동배정물을 말하는 경우,
- 만약 그것이 자유로워진 동적 메모리의 블록 안에 있는 물체를 가리킬 경우.
첫 번째는 참조에 정적 범위 지정이 있는 경우 자동으로 탐지하기 쉽지만 참조가 동적으로 할당된 개체의 멤버인 경우에는 여전히 문제가 되고 두 번째는 탐지하기가 더 어렵습니다.이것들은 참고문헌과 관련된 유일한 문제이며, 합리적인 할당 정책에 의해 적절하게 해결됩니다.
참조의 용도
- 포인터를 유용하게 대체하는 것 외에 참조를 편리하게 사용할 수 있는 하나의 응용 프로그램은 함수 매개 변수 목록에 있으며, 여기서 호출자가 명시적인 주소를 사용하지 않고 출력에 사용되는 매개 변수를 전달할 수 있습니다.예를 들어,
공허한 광장(인트 x, 인트& 아웃_슬라이스) { 아웃_슬라이스 = x * x; }
그러면 다음 호출에서 y에 9를 입력합니다.
인트 y; 광장(3, y);
그러나 lvalue reference parameters가 다음과 같이 지정되지 않았기 때문에 다음 호출은 컴파일러 오류를 줄 것입니다.const
주소 지정 가능한 값에만 바인딩할 수 있습니다.
광장(3, 6);
- alvalue reference를 반환하면 함수 호출을 다음에 할당할 수 있습니다.
인트& 프레인크(인트& x) { 돌아가다 ++x; // "return x++;"이(가) 잘못되었을 것입니다. } 프레인크(y) = 5; // ++y, y = 5와 같음
- 많은 구현에서 일반적인 매개변수 전달 메커니즘은 종종 큰 매개변수에 대한 값비싼 복사 작업을 의미합니다.다음과 같이 자격이 부여된 참조
const
이 오버헤드를 방지하는 함수 간에 큰 개체를 전달하는 유용한 방법입니다.공허한 F슬로우(Big Object x) { /* ... */ } 공허한 에프패스트(머리를 짜다 Big Object& x) { /* ... */ } Big Object y; F슬로우(y); // 천천히, 매개변수 x에 y를 복사합니다. 에프패스트(y); // 빠름, y에 대한 직접 읽기 전용 액세스를 제공합니다.
한다면FFast
실제로 수정할 수 있는 고유한 x의 복사본이 필요하므로 명시적으로 복사본을 만들어야 합니다.포인터를 사용하여 동일한 기법을 적용할 수 있지만, 이는 함수의 모든 호출 사이트를 수정하여 (의 번거로운 주소-)를 추가하는 것을 수반합니다.&
) 인수 연산자이며, 나중에 개체가 작아질 경우 취소하기도 어렵습니다.
다형성 거동
(C++ 맥락에서) 참조와 포인터 사이의 관계를 지속하면, 전자는 사람이 예상할 수 있는 것처럼 다형성 기능을 보여줍니다.
#허튼소리 <아이오스트림> 학급 A { 일반의: A() = 체납; 가상의 공허한 프린트() { std::꾸트린 << A반입니다.\n"; } }; 학급 B : 일반의 A { 일반의: B() = 체납; 가상의 공허한 프린트() { std::꾸트린 << "여기는 B반입니다.\n"; } }; 인트 주된() { A a; A& ref_to_a = a; B b; A& ref_to_b = b; ref_to_a.프린트(); ref_to_b.프린트(); }
위의 소스는 유효한 C++이며 다음과 같은 출력을 생성합니다.
이것은 A반 입니다. 여기는 B반입니다.
외부 링크
- C++ FAQ Lite의 참조 사항
- 출고참조설명
- Joint Technical Committee ISO/IEC JTC 1, Subcommittee SC 22, Working Group WG 21. International Standard ISO/IEC 14822 (PDF).
{{cite book}}
Joint Technical Committee ISO/IEC JTC 1, Subcommittee SC 22, Working Group WG 21. International Standard ISO/IEC 14822 (PDF).{{cite book}}
무시됨work=
(도움말)CS1 maint: 여러 이름: 저자 목록 (링크)
참고문헌
- ^ ISO/IEC 14822, 조항 9.3.3.2, 1항.
- ^ Sutter, Herb; Stroustrup, Bjarne; Dos Reis, Gabriel. "Forwarding References" (PDF).
- ^ ISO/IEC 14822, 조항 13.10.2.1항, 3항.
- ^ Becker, Thomas. "C++ Rvalue References Explained". Retrieved 2022-11-25.