2017-02-15 1 views
3

여러 패키지가 포함 된 프로젝트에서 작업하고 있습니다. 그 (여기가 전체 코드입니다)처럼 내 기본 패키지 중 하나에서 나는, 스마트 포인터를 선언Delphi - 스마트 포인터 생성자로 이상한 동작이 발생했습니다.

나중에 내 프로젝트에
unit UTWSmartPointer; 

interface 

type 
    IWSmartPointer<T> = reference to function: T; 

    TWSmartPointer<T: class, constructor> = class(TInterfacedObject, IWSmartPointer<T>) 
    private 
     m_pInstance: T; 

    public 
     constructor Create; overload; virtual; 

     constructor Create(pInstance: T); overload; virtual; 

     destructor Destroy; override; 

     function Invoke: T; virtual; 
    end; 

implementation 
//--------------------------------------------------------------------------- 
constructor TWSmartPointer<T>.Create; 
begin 
    inherited Create; 

    m_pInstance := T.Create; 
end; 
//--------------------------------------------------------------------------- 
constructor TWSmartPointer<T>.Create(pInstance: T); 
begin 
    inherited Create; 

    m_pInstance := pInstance; 
end; 
//--------------------------------------------------------------------------- 
destructor TWSmartPointer<T>.Destroy; 
begin 
    m_pInstance.Free; 
    m_pInstance := nil; 

    inherited Destroy; 
end; 
//--------------------------------------------------------------------------- 
function TWSmartPointer<T>.Invoke: T; 
begin 
    Result := m_pInstance; 
end; 
//--------------------------------------------------------------------------- 

end. 

(다른 패키지), 나는 GDI + 개체 (함께이 스마트 포인터를 사용 TGpGraphicsPath). 다음과 같은 그래픽 경로를 선언합니다.

... 
pGraphicsPath: IWSmartPointer<TGpGraphicsPath>; 
... 
pGraphicsPath := TWSmartPointer<TGpGraphicsPath>.Create(); 
... 

그러나 코드를 실행할 때 화면에 아무 것도 그려지지 않습니다. 나는 오류도 예외도 액세스 위반도 없으며 단지 빈 페이지 만 얻는다. 하지만 난 그냥 그런 코드를 변경하는 경우 :

... 
pGraphicsPath: IWSmartPointer<TGpGraphicsPath>; 
... 
pGraphicsPath := TWSmartPointer<TGpGraphicsPath>.Create(TGpGraphicsPath.Create); 
... 

은 모두가 잘되고, 예상대로 내 경로가 정확하게 그려집니다. 하지만 첫 번째 생성자가 예상대로 작동하지 않는 이유를 알 수 없습니다. 누군가 나에게이 이상한 행동을 설명 할 수 있니?

감사합니다.

+3

TGpGraphicsPath이 TObject.Create가 대신 호출되는 경우 선택적 인수를 취한다는 내 생각 엔 것 TGpGraphicsPath.Create (optionalArgument) - 제네릭을주의해야합니다. – Dsm

+1

@Remy 익명 메소드가 실제로 인터페이스라는 사실이 밝혀졌습니다.이 구현 방법은 다음과 같이 널리 사용됩니다. http://stackoverflow.com/a/39955320 –

+0

@Dsm'TGpGraphicsPath'에는 생성자에 매개 변수가 없습니다. . –

답변

4

이것은 매우 복잡한 함정입니다. 당신이 쓸 때 :

TGpGraphicsPath.Create 

당신은 매개 변수없는 생성자를 호출하고 있다고 생각할 수 있습니다. 그러나 그렇지 않습니다. 실제로이 생성자를 호출하고 있습니다.

constructor Create(fillMode: TFillMode = FillModeAlternate); reintroduce; overload;  

기본값을 제공하므로 컴파일러에서 기본값을 제공합니다. 스마트 포인터 클래스에서

당신은 쓰기 :

T.Create 

이 정말 매개 변수가없는 생성자를 호출한다. 하지만 그것은 TObject에 의해 정의 된 생성자입니다. 해당 생성자를 사용하면 TGPGraphicsPath 인스턴스가 제대로 초기화되지 않습니다.

constructor 제네릭 제약 조건을 사용하려는 경우 매개 변수없는 생성자로 올바르게 구성 할 수있는 클래스를 항상 사용해야합니다. 불행히도 TGPGraphicsPath은 청구서에 맞지 않습니다. 실제로 그러한 수업은 우세합니다.

명시 적으로 생성자를 호출하지 않으려면 여기에서 할 수있는 일이 많지 않습니다. 스마트 포인터 클래스가이 특정 클래스에 대해 호출 할 생성자를 찾아내는 것은 거의 불가능합니다.

제 조언은 constructor 제네릭 제약에서 벗어나 스마트 포인터 클래스의 소비자가 명시 적으로 인스턴스를 인스턴스화하도록하는 것입니다.

꽤 일반적인 문제입니다 - 내가 적은보다는 일주일 전에 여기에 비슷한 질문에 대답 : Why does a deserialized TDictionary not work correctly?

+0

generics/constraints의 원래 개발자가 수행하는 매우 불쾌한 실수와 ctors가 자동으로 상속되는 C#에서 복사하는 실수. ctor 제약 조건을 사용하면 거기에 의미가 있으며, 조상이 가질 수있는 매개 변수가없는 클래스를 사용하는 것을 실제로 방지합니다. Delphi에서 컴파일러는 자식 클래스가 오버로드가 추가 된 ctors를 가지 자마자 TObject ctor를 봅니다. 하지만 이제는 컴파일러가이 문제에서 TObject ctor를 볼 수 없기 때문에 적절한 과부하 해결을 수행하여이 문제를 해결할 수 있다고 생각했습니다. –

+0

@stefan 기본 설계가 크게 변경되지 않는 한 컴파일러가 매개 변수없는 생성자로 기본 arg로 한 param 생성자를 처리 할 수 ​​있다고 생각하지 않습니다. –

+0

컴파일러가 형식에서 사용 가능한 생성자를 단순히 볼 수없는 이유와 매개 변수 생성자가없는 경우 모든 기본값 매개 변수가있는 생성자를 찾는 이유가 없습니다. 사용자 코드는 두 방법 모두 동일하며 일반적으로 Generics 외부에서는 정상적으로 작동하므로 컴파일러는 코드가 Generic인지 아닌지에 관계없이 올바른 작업을 수행해야합니다. 이 변경은 사용자 정의 코드에 의해 입력 매개 변수를 필요로하지 않는 생성자를 포함하기 위해 그 정의가 약간 느슨해지면'constructor' 제약 조건을 깨뜨려서는 안됩니다. –

관련 문제