먼저 우리는 가치와 참조로 전달한다는 의미로 돌아 가야합니다.
Java 및 SML과 같은 언어의 경우 모든 변수가 스칼라이고 복사 의미론이 내장되어 있으므로 변수 값을 복사하는 것처럼 값으로 전달하는 것이 간단합니다 (참조에 의한 전달도 없음). C ++ 또는 "참조"(이름과 구문이 다른 포인터)로 입력하십시오.
C에는 스칼라 및 사용자 정의 유형이 있습니다.
- 스칼라에는 복사되는 숫자 또는 추상 값 (포인터가 숫자가 아니며 추상 값이 있음)이 있습니다.
- 집계 유형에는 초기화 가능한 모든 멤버가 복사됩니다.
- 제품 유형 (배열 및 구조)의 경우 : 재귀 적으로 모든 구조의 멤버 및 배열 요소가 복사됩니다 (C 함수 구문은 배열을 값으로 직접 전달할 수는 없지만 구조체의 배열 만 전달할 수는 있지만 세부 사항입니다) ).
- 합계 유형 (유니언)의 경우 : "활성 멤버"의 값이 유지됩니다. 분명히, 멤버 별 복사는 모든 멤버를 초기화 할 수있는 순서가 아닙니다.
C ++에서 사용자 정의 유형은 사용자 정의 사본 의미론을 가질 수 있으며, 이는 자원 소유권과 "딥 카피"작업을 통해 객체를 사용한 진정한 "객체 지향"프로그래밍을 가능하게합니다. 이러한 경우 복사 작업은 실제로 임의 작업을 거의 수행 할 수있는 함수를 호출하는 것입니다.
C ++로 컴파일 된 C 구조체의 경우 "복사"는 여전히 컴파일러가 암시 적으로 생성 한 사용자 정의 복사 작업 (생성자 또는 할당 연산자)을 호출하는 것으로 정의됩니다. 이는 C / C ++ 공통 서브 세트 프로그램의 의미가 C와 C ++에서 다르다는 것을 의미합니다. C에서는 전체 집계 유형이 복사되고 C ++에서는 내재적으로 생성 된 복사 함수가 호출되어 각 멤버를 복사합니다. 최종 결과는 두 경우 모두 각 멤버가 복사되는 것입니다.
(조합 내부의 구조체가 복사 될 때 예외가 있다고 생각합니다.)
따라서 클래스 유형의 경우 새 인스턴스를 만드는 유일한 방법 (유니온 복사본 외부)은 생성자를 사용하는 것입니다 (사소한 컴파일러 생성 생성자를 가진 경우에도).
단항 연산자를 통해 rvalue의 주소를 사용할 수 &
는 없지만 rvalue 객체가 없다는 의미는 아닙니다. 및 목적은, 정의에 의해, 어드레스를 가진다 ; 그리고 그 주소는 심지어 구문 구문으로 표현된다 : 클래스 타입의 객체는 생성자에 의해서만 생성 될 수 있고, this
포인터를 가진다; 그러나 사소한 유형의 경우 사용자 작성 생성자가 없으므로 this
사본을 구성하고 이름 을 지정할 때까지 넣을 장소가 없습니다 .
스칼라 유형의 경우 객체의 값은 객체의 rvalue, 객체에 저장된 순수한 수학적 값입니다.
클래스 유형의 경우 객체 값의 유일한 개념은 객체의 다른 사본입니다. 복사 생성자, 실제 함수로만 만들 수 있습니다 (함수가 너무 사소한 사소한 유형의 경우 때때로 생성자를 호출하지 않고 생성). 즉 , 객체의 값은 실행에 의한 전역 프로그램 상태 변경의 결과입니다 . 수학적으로 액세스하지 않습니다.
따라서 값으로 전달하는 것은 실제로 중요하지 않습니다. 복사 생성자 호출에 의해 전달됩니다 . 복사 생성자는 내부 불변 (내재적 C ++ 속성이 아닌 추상 사용자 속성)을 고려하여 오브젝트 유형의 적절한 의미에 따라 적절한 "복사"조작을 수행해야합니다.
클래스 객체의 값으로 전달은 다음을 의미합니다.
- 다른 인스턴스를 만들
- 그런 다음 호출 된 함수가 해당 인스턴스에서 작동하도록합니다.
이 문제는 복사본 자체가 주소를 가진 객체인지 여부와 관련이 없습니다. 모든 함수 매개 변수는 객체이며 주소를 가지고 있습니다 (언어 시맨틱 레벨).
문제는 :
- 카피는 스칼라와 같이 원래 객체의 순수 수학 값 (true pure rvalue)으로 초기화 된 새 객체입니다 .
- 또는 copy는 클래스와 마찬가지로 original object의 값입니다 .
사소한 클래스 유형의 경우에도 원본의 멤버 사본 멤버를 정의 할 수 있으므로 사소한 복사 작업 (복사 생성자 및 할당)으로 인해 원본의 순수한 rvalue를 정의 할 수 있습니다. 임의의 특수 사용자 기능으로는 그렇지 않습니다. 원본의 값은 생성 된 사본이어야합니다.
클래스 객체는 호출자가 구성해야합니다. 생성자는 공식적으로 this
포인터를 가지고 있지만 형식은 여기에 관련이 없습니다. 모든 객체는 공식적으로 주소를 가지고 있지만 실제로 *&i = 1;
는 순수하게 로컬이 아닌 방식으로 주소를 얻는 객체 만 (순수하게 로컬 주소를 사용하는 것과는 달리 ) 잘 정의되어 있어야합니다 주소.
개별적으로 컴파일 된이 두 함수 모두에 주소가있는 것처럼 보이는 경우, 반드시 주소를 통해 전달해야합니다.
void callee(int &i) {
something(&i);
}
void caller() {
int i;
callee(i);
something(&i);
}
여기에 경우에도이 something(address)
순수 함수 또는 매크로이든 (처럼 printf("%p",arg)
주소를 저장하거나 다른 기업에 통신 할 수 없습니다) 주소가 아니라 고유의 객체에 대해 정의해야하기 때문에, 우리는 주소로 전달하는 요구 사항이 int
독특한있다 정체.
전달 된 주소의 관점에서 외부 함수가 "순수한"지 여부는 알 수 없습니다.
여기서 발신자 가 아닌 간단한 생성자 또는 소멸자 에서 주소를 실제로 사용할 가능성 은 아마도 안전하고 간단한 경로를 취하고 발신자에게 객체에 정체성을 부여하고 주소를 전달 하는 이유 일 것입니다. 확인 생성자의 주소가 아닌 사소한 사용, 건축 후 소멸자하는 것은 일관성이 있음 : 객체의 존재를 통해 동일하게 표시되어야합니다.this
다른 함수와 마찬가지로 사소하지 않은 생성자 또는 소멸자는 this
사소한 것이 아닌 일부 객체는 다음과 같은 경우에도 해당 값보다 일관성이 필요한 방식으로 포인터를 사용할 수 있습니다 .
struct file_handler { // don't use that class!
file_handler () { this->fileno = -1; }
file_handler (int f) { this->fileno = f; }
file_handler (const file_handler& rhs) {
if (this->fileno != -1)
this->fileno = dup(rhs.fileno);
else
this->fileno = -1;
}
~file_handler () {
if (this->fileno != -1)
close(this->fileno);
}
file_handler &operator= (const file_handler& rhs);
};
이 경우 포인터를 명시 적으로 사용하더라도 (명시 적 구문 this->
) 객체 ID는 관련이 없습니다. 컴파일러는 객체를 비트 단위로 복사하여 객체를 이동하고 "복사 제거"를 수행 할 수 있습니다. 이것은 this
특별한 멤버 함수에서 의 사용의 "순도"수준을 기반으로합니다 (주소는 이스케이프되지 않습니다).
그러나 순도는 표준 선언 수준에서 사용할 수있는 속성이 아닙니다 (비 인라인 함수 선언에 순도 설명을 추가하는 컴파일러 확장이 존재 함) 사용할 수없는 코드 순도에 따라 ABI를 정의 할 수 없습니다 (코드는 인라인이 아니고 분석에 사용 가능할 수 있습니다).
순도는 "확실히 순수한"또는 "불순하거나 알려지지 않은"것으로 측정됩니다. 공통 접지 또는 의미의 상한 (실제 최대) 또는 LCM (최소 공통 배수)은 "알 수 없음"입니다. 따라서 ABI는 알려지지 않은 상태로 정착합니다.
요약:
- 일부 구문에서는 컴파일러가 객체 ID를 정의해야합니다.
- ABI는 프로그램 클래스의 용어로 정의되며 최적화 될 수있는 특정 사례는 아닙니다.
가능한 미래의 일 :
순도 주석은 일반화 및 표준화하기에 충분히 유용합니까?