답변:
그것은 일반적으로 가장 좋은 방법은 추천하는 데 사용 1 에 대한 const를 심판하여 사용 패스 모든 종류의 유형 (내장을 제외하고 char
, int
, double
반복자를 들어, 등), 함수 객체에 대한 (에서 파생 람다, 클래스 std::*_function
).
이것은 이동 의미론 이 존재하기 전에 특히 그렇습니다 . 그 이유는 간단합니다. 값으로 전달하면 객체의 복사본을 만들어야했으며 매우 작은 객체를 제외하고는 참조를 전달하는 것보다 항상 비쌉니다.
C ++ 11을 사용하면 이동 의미론 을 얻었습니다 . 간단히 말해서 이동 의미론은 어떤 경우에는 객체를 복사하지 않고 "값으로"전달할 수 있도록합니다. 특히, 전달하는 객체가 rvalue 인 경우 입니다.
그 자체로 객체를 이동하는 것은 여전히 참조로 전달하는 것만 큼 비싸다. 그러나 많은 경우 함수는 내부적으로 객체를 복사합니다. 즉 , 인수의 소유권 을 갖습니다. 2
이러한 상황에서 우리는 다음과 같은 (단순화 된) 장단점이 있습니다.
객체가 rvalue가 아닌 한 "값별 패스"는 여전히 객체를 복사합니다. rvalue의 경우 객체를 대신 이동할 수 있으므로 두 번째 경우가 더 이상 "복사, 이동"하지 않고 "이동 한 다음 (잠재적으로) 다시 이동"됩니다.
적절한 이동 생성자 (벡터, 문자열 등)를 구현하는 큰 객체의 경우 두 번째 경우는 첫 번째보다 훨씬 더 효율적입니다. 따라서 함수가 인수의 소유권을 가져 오거나 객체 유형이 효율적인 이동을 지원하는 경우 값으로 전달 을 사용 하는 것이 좋습니다 .
역사적 메모 :
실제로 모든 최신 컴파일러는 값으로 전달하는 것이 비쌀 때 알아낼 수 있어야하며 가능하면 const 참조를 사용하도록 호출을 암시 적으로 변환해야합니다.
이론에 의하면. 실제로 컴파일러는 함수의 이진 인터페이스를 중단하지 않고 항상 이것을 변경할 수는 없습니다. 일부 특수한 경우 (함수가 인라인 될 때) 컴파일러가 함수의 조치를 통해 원래 오브젝트가 변경되지 않음을 알 수있는 경우 사본이 실제로 제거됩니다.
그러나 일반적으로 컴파일러는 이것을 결정할 수 없으며 C ++에서 이동 의미의 출현으로 인해이 최적화가 훨씬 덜 적합 해졌습니다.
1 예 : Scott Meyers의 효과적인 C ++ .
2 이것은 객체 생성자의 경우에 특히 그렇습니다. 인수는 인수를 받아서 내부에 생성 된 객체 상태의 일부로 저장할 수 있습니다.
편집 : cpp-next의 Dave Abrahams의 새 기사 :
복사가 저렴한 구조체의 값으로 전달하면 컴파일러가 객체가 별칭이 아닌 것으로 간주 할 수 있다는 추가 이점이 있습니다 (동일한 객체가 아님). 패스 바이 레퍼런스를 사용하면 컴파일러는 항상이를 가정 할 수 없습니다. 간단한 예 :
foo * f;
void bar(foo g) {
g.i = 10;
f->i = 2;
g.i += 5;
}
컴파일러는 그것을 최적화 할 수 있습니다
g.i = 15;
f->i = 2;
f와 g가 같은 위치를 공유하지 않는다는 것을 알고 있기 때문입니다. 만약 g가 참조 (foo &)라면, 컴파일러는 그것을 추측 할 수 없었습니다. gi는 f-> i로 별칭을 지정할 수 있고 값은 7이어야하므로 컴파일러는 메모리에서 새로운 gi 값을 다시 가져와야합니다.
보다 실용적인 규칙을 보려면 Move Constructors 기사 (권장 사항) 에서 찾아 볼 수있는 규칙이 있습니다.
위의 "기본"은 기본적으로 몇 바이트 길이이고 다형성 (반복자, 함수 객체 등)이 아니거나 복사 비용이 많이 들지 않는 작은 데이터 유형을 의미합니다. 그 논문에는 또 다른 규칙이 있습니다. 아이디어는 때로는 복사를 원하고 (인수를 수정할 수없는 경우) 때로는 원하지 않는 경우 (인수가 어쨌든 인수 인 경우 함수에서 인수 자체를 사용하려는 경우) 예를 들어). 이 문서는 그 방법을 자세히 설명합니다. C ++ 1x에서이 기술은 기본적으로 언어 지원과 함께 사용될 수 있습니다. 그때까지는 위의 규칙을 따릅니다.
예 : 문자열을 대문자로 만들고 대문자 버전을 반환하려면 항상 값을 전달해야합니다. 어쨌든 사본을 가져와야합니다 (const 참조를 직접 변경할 수는 없습니다)-가능한 한 투명하게 만드는 것이 좋습니다. 발신자가이 문서에 자세히 설명 된대로 최대한 많이 최적화 할 수 있도록 사본을 조기에 만드십시오.
my::string uppercase(my::string s) { /* change s and return it */ }
그러나 어쨌든 매개 변수를 변경할 필요가 없으면 const를 참조하여 가져 오십시오.
bool all_uppercase(my::string const& s) {
/* check to see whether any character is uppercase */
}
그러나 매개 변수의 목적이 인수에 무언가를 쓰는 것이라면 상수가 아닌 참조로 전달하십시오.
bool try_parse(T text, my::string &out) {
/* try to parse, write result into out */
}
__restrict__
과도한 사본을 사용하는 것보다 GCC (참조 작업도 가능)를 사용하고 싶습니다 . 너무 나쁜 표준 C ++에서는 C99의 restrict
키워드를 채택하지 않았습니다 .
지적했듯이 유형에 따라 다릅니다. 기본 제공 데이터 형식의 경우 값별로 전달하는 것이 가장 좋습니다. int 쌍과 같은 매우 작은 구조조차도 값을 전달함으로써 더 잘 수행 할 수 있습니다.
다음은 예입니다. 정수 값이 있고 다른 루틴으로 전달하려고한다고 가정하십시오. 해당 값이 레지스터에 저장되도록 최적화 된 경우 참조로 전달하려면 먼저 메모리에 저장 한 다음 스택에 배치 된 해당 메모리에 대한 포인터를 호출하여 호출을 수행해야합니다. 값으로 전달 된 경우 필요한 것은 레지스터가 스택에 푸시 된 것입니다. (세부 사항은 다른 호출 시스템과 CPU에 비해 조금 더 복잡합니다).
템플릿 프로그래밍을 수행하는 경우 일반적으로 전달되는 유형을 모르기 때문에 항상 const 참조로 전달해야합니다. 값으로 나쁜 것을 전달하는 것에 대한 처벌 통과는 내장 유형을 전달하는 처벌보다 훨씬 나쁩니다. const ref.
이것은 템플릿이 아닌 함수의 인터페이스를 디자인 할 때 일반적으로 작동하는 것입니다.
함수가 매개 변수를 수정하지 않으려는 경우 값을 복사하면 값이 복사하기에 저렴한 경우 (int, double, float, char, bool 등) std :: string, std :: vector 및 나머지는 표준 라이브러리의 컨테이너 중 하나가 아닙니다)
값을 복사하는 데 비용이 많이 들고 함수가 가리키는 값을 수정하지 않으려는 경우 const 포인터로 전달하고 NULL은 함수가 처리하는 값입니다.
값이 복사하는 데 비용이 많이 들고 함수가 가리키는 값을 수정하려고하고 NULL이 함수가 처리하는 값인 경우에는 상수가 아닌 포인터로 전달하십시오.
값이 복사하는 데 비용이 많이 들고 함수가 참조되는 값을 수정하지 않고 포인터가 대신 사용 된 경우 NULL은 유효한 값이 아닌 경우 const 참조로 전달하십시오.
값이 복사하기에 비싸고 함수가 참조 된 값을 수정하려고하고 포인터가 대신 사용 된 경우 NULL은 유효한 값이 아닌 경우 비정규 참조로 전달하십시오.
std::optional
그림에 추가 하면 더 이상 포인터가 필요하지 않습니다.
답을 얻은 것 같습니다. 값으로 전달하는 것은 비용이 많이 들지만 필요한 경우 사용할 사본을 제공합니다.
const 참조를 통과하는 규칙이 더 좋습니다. 그러나 함수 인수를 로컬로 수정 해야하는 경우 값을 전달하는 것이 좋습니다. 일부 기본 유형의 경우 성능은 일반적으로 값과 참조로 전달하는 데 동일합니다. 실제로 내부적으로 포인터로 표시되는 참조이므로 포인터의 경우 두 전달이 모두 성능면에서 동일하거나 값을 전달하는 것이 불필요한 역 참조로 인해 더 빠를 수 있다고 예상 할 수 있습니다.
경험상 비 클래스 유형의 값과 클래스의 const 참조입니다. 클래스가 실제로 작은 경우 값으로 전달하는 것이 더 좋지만 차이는 최소화됩니다. 실제로 피하고 싶은 것은 값으로 거대한 클래스를 전달하고 모두 복제하는 것입니다. 예를 들어 std :: vector를 상당히 많은 요소로 전달하면 큰 차이가 있습니다.
std::vector
실제로 해당 항목을 힙에 할당하고 벡터 객체 자체는 절대 커지지 않는다는 것입니다. 아 잠깐만 그러나 연산으로 인해 벡터 사본이 만들어지면 실제로 모든 요소가 복제됩니다. 나쁘다.
sizeof(std::vector<int>)
상수이지만 값으로 전달하면 컴파일러 영리성이없는 경우에도 내용을 복사합니다.
작은 유형의 값으로 전달하십시오.
큰 유형에 대한 const 참조에 의한 전달 (big의 정의는 시스템마다 다를 수 있음) 그러나 C ++ 11에서는 이동 의미론을 이용할 수 있으므로 데이터를 소비하려는 경우 값을 기준으로 전달합니다. 예를 들면 다음과 같습니다.
class Person {
public:
Person(std::string name) : name_(std::move(name)) {}
private:
std::string name_;
};
이제 호출 코드는 다음을 수행합니다.
Person p(std::string("Albert"));
그리고 하나의 객체 만 만들어 name_
클래스의 멤버로 직접 이동합니다 Person
. const 참조로 전달하면에 삽입하기 위해 사본을 만들어야합니다 name_
.
간단한 차이점 :-함수에 입력 및 출력 매개 변수가 있으므로 전달하는 입력 및 출력 매개 변수가 동일한 경우 입력 및 출력 매개 변수가 다른 경우 참조로 호출을 사용하고 값으로 호출하는 것이 좋습니다.
예 void amount(int account , int deposit , int total )
입력 매개 변수 : 계정, 예금 출력 매개 변수 : 총
입력과 출력은 vaule에 의한 다른 사용 호출입니다
void amount(int total , int deposit )
입력 총 입금 출력 총