2010-05-25 6 views
5

입니다 무엇보다도 호기심 ... 다음과 같이 컴파일러는 C++ 클래스의 메서드를 어디에 저장합니까?

한다고 가정

가 나는 C++ 클래스 키티가 있습니다

class Kitty 
{ 
    void Meow() 
    { 
     //Do stuff 
    } 
} 

키티의 모든 인스턴스() 야옹에 대한 코드를 컴파일러 자리를합니까?

분명히 모든 곳에서 동일한 코드를 반복하면 더 많은 메모리가 필요합니다. 그러나 다른 한편으로는 가까운 메모리의 상대 위치로 분기하는 것은 현대 프로세서의 메모리에서 절대 위치로 분기하는 것보다 더 적은 어셈블리 명령어를 필요로하기 때문에 잠재적으로 더 빠릅니다.

이것은 구현 세부 사항이라고 생각합니다. 따라서 다른 컴파일러가 다르게 수행 할 수 있습니다.

여기에 정적 또는 가상 메소드를 고려하지 않습니다.

+0

일반적으로 알려진 질문에 대한 답을 얻기 위해 준비하십시오 (+1해도 좋은 질문입니다 :). –

+0

명확히하기 위해 인라인에 관심이 없습니다. 나는 그것이 어떻게 작동하는지 알고있다. – Mashmagar

답변

3

저는 인스턴스 메서드의 표준 방법이 정적 메서드처럼 한 번만 구현되지만 호출을 수행하려면 this 포인터를 특정 레지스터 나 스택에 전달해야한다고 생각합니다.

1

아니요, 컴파일러는 Meow 코드를 한 번만 생성하고 각 Kitty 인스턴스는 멤버가 라인 외부 컴파일 된 경우에만 사용합니다. 컴파일러가 함수를 인라인 할 수 있고 그 함수를 인라인으로 선택하면 (Kitty의 모든 인스턴스가 아닌) 각 사용 지점에서 복제됩니다.

4

일반적인 구현에서는 지정된 함수의 복사본이 하나뿐입니다. 지정된 개체 인스턴스에 대한 코드와 데이터 간의 연결은 개체 인스턴스 (및 해당 데이터)에 대한 포인터 인 숨겨진 매개 변수 (함수에 this라고 함)를 전달하여 설정됩니다.

가상 함수의 경우, 각 클래스는 가상 함수에 대한 포인터 집합을 보유하는 vtable을 가져오고 각 객체는 해당 클래스의 vtable에 대한 포인터를 가져옵니다. 가상 함수는 vtable 포인터를 찾고, 올바른 오프셋을보고, 해당 포인터가 가리키는 함수를 호출하여 호출됩니다.

+0

올바르게 이해한다면, 파이썬이 모든 멤버 함수에서 명시 적으로 수행하는 작업을 인스턴스에 대한 참조가 필요합니다. 예? – Mashmagar

+0

멤버 함수 또는 멤버 변수에 액세스 할 때 명시 적으로 'this' 포인터를 사용하더라도 관계없이 항상 사용됩니다. 멤버 함수의 경우, 그것은 'this'를 멤버 함수에 첫 번째 (보이지 않는) 매개 변수로 전달하는 것을 의미합니다. 가상 함수 인 경우 가상 테이블은 복잡하지만 본질적으로 항상 'this' 포인터를 첫 번째 매개 변수로 멤버 함수에 전달합니다. 그래서 특정 연산자 오버로드 (예 :'<<')는 멤버 함수가 아닌 friend 함수 여야합니다. –

+0

@Mashmagar : 그렇습니다,이 두 가지는 상당히 비슷합니다. –

2

아니요, 그렇지 않습니다.
virtual이 아닌 메서드는 다른 함수와 완전히 같지만 this 포인터에 대한 추가 인수가 있습니다.

virtual의 방법은 invoked using a v-table입니다. v-table은 오브젝트 데이터 옆에 저장된 함수 포인터의 목록입니다. 어떤 점에서 이것은 당신이 묘사하는 것에 더 가깝지만 여전히 기능의 몸체는 모든 객체 인스턴스에 대해 항상 동일합니다.
메서드에 static 변수가있는 경우이를 증명할 수 있습니다. 정적 변수는 다른 인스턴스에서 호출 된 메소드에 대해 동일하게됩니다.

+0

사실, 대부분의 구현은 * 포인터 *를 vtable 자체가 아니라 객체의 vtable에 배치합니다. 이 포인터는 종종 * vptr *라고합니다. – fredoverflow

0

컴파일러는 자체 데이터 구조 안에 모든 클래스 (객체 아님)에 대한 항목을 만듭니다. 클래스에 대한이 항목에는 해당 클래스의 메소드에 대한 포인터가 들어 있습니다.

개체는 부모 클래스에 대한 포인터와 해당 인스턴스 필드의 컬렉션으로 표현됩니다 (모든 개체에 대해 서로 다르기 때문에). 그런 다음 메서드가 호출 될 때 개체는 부모 개체에 대한 포인터를 따릅니다. 그런 다음 적절한 메소드에 대한 포인터를 따르십시오. 객체에 대한 포인터도 메소드에 제공되며이 포인터는이 포인터 역할을합니다.

가상 메서드는 좀 더 복잡하지만 비슷한 방식으로 수행됩니다.

자세한 내용은 프로그래밍 언어 클래스를 사용할 수 있는지 확인하십시오.

여기를 설명하는 ASCII 아트에서 가난한 시도는 다음과 같습니다

obj      class 
+------------+   +----------+ 
| ptrToClass |----------->| method1 | ----------> toSomewhere(ptrToObj) 
|------------|   |----------| 
| field1  |   | method2 | ----------> toSomewhereElse(ptrToObj) 
+------------+   +----------+ 
2

당신이 클래스 정의 내부 Meow의 정의를 가지고 있기 때문에, Meow 암시 적으로 인라인입니다.

인라인은 컴파일러에서 호출을 실제 함수 내용으로 바꾸라는 힌트입니다. 그러나 힌트 일뿐입니다. 컴파일러는 힌트를 무시할 수도 있습니다.

컴파일러가 힌트를 준수하면 각 호출이 함수 내용으로 바뀝니다. 즉, 컴파일러는 함수 호출을 생성하는 대신 Meow이 호출 될 때마다 코드를 생성합니다.

컴파일러가 힌트를 무시하면 컴파일러/링커는 모든 호출이 전달되는 단일 버전이되도록 준비합니다 (인라인이기 때문에 함수를 사용하는 모든 번역 단위가 하나의 버전 만 유지하라는 지시와 함께 별도의 사본을 얻으십시오).

마지막으로 함수가 인라인이 아닌 설명으로 이동해 봅시다. 이 경우 코더는 정의가 정확하게 하나의 번역 단위로 나타나고 모든 호출이이 한 버전으로 전송되도록해야합니다.

관련 문제