2012-01-03 73 views
8

Tôi thường lập trình trong C# nhưng đang cố gắng thực hiện một chút C++ và đang gặp khó khăn khi cố gắng triển khai các giao diện trong C++.Thực hiện các giao diện trong C++

Trong C# Tôi muốn làm điều gì đó như thế này:

class Base<T> 
{ 
    public void DoSomething(T value) 
    { 
     // Do something here 
    } 
} 

interface IDoubleDoSomething 
{ 
    void DoSomething(double value); 
} 

class Foo : Base<double>, IDoubleDoSomething 
{ 
} 

Trong C++ Tôi đã thực hiện nó như thế này:

template <class T> 
class Base 
{ 
public: 
    virtual void DoSomething(T value) 
    { 
     // Do something here 
    } 
}; 

class IDoubleDoSomething 
{ 
public: 
    virtual void DoSomething(double value) = 0; 
}; 

class Foo : public Base<double>, public IDoubleDoSomething 
{ 
}; 

Vấn đề là tôi không thể nhanh chóng Foo vì nó là trừu tượng (không triển khai DoSomething). Tôi nhận ra tôi có thể thực hiện DoSomething và chỉ cần gọi phương thức trên cơ sở nhưng tôi đã hy vọng có một cách tốt hơn để làm điều này. Tôi có các lớp khác mà kế thừa từ cơ sở với các loại dữ liệu khác nhau và tôi có các lớp khác mà kế thừa từ IDoubleDoSomething mà không sử dụng Base.

Bất kỳ trợ giúp đánh giá cao.

+2

là gì quá tệ với điều này, và bằng cách nào đó có thể là "tốt hơn"? –

+2

Tôi cho rằng "tốt hơn" sẽ hoạt động giống như phiên bản C# trong đó 'Foo' kế thừa phương thức từ Cơ sở và không cần thực hiện bất kỳ. Tôi đoán không có gì thực sự xấu với việc thực hiện một cách thực hiện của từng phương pháp từ Base trong Foo, nó chỉ cảm thấy lộn xộn. – Clueless

+0

Đó là lộn xộn, nhưng tôi chưa bao giờ tìm thấy một cách tốt hơn: ( –

Trả lời

1

Như những người khác đã lưu ý, hai hàm trong lớp cơ sở không liên quan (mặc dù có cùng tên và kiểu đối số), vì chúng không có lớp cơ sở chung. Nếu bạn muốn họ có liên quan, bạn cần phải cung cấp cho họ một lớp cơ sở chung.

Ngoài ra, nói chung, nếu bạn muốn nhiều thừa kế hoạt động đúng, bạn cần khai báo các lớp cơ sở không riêng tư của mình là virtual. Nếu không, nếu bạn đã từng có các lớp cơ sở chung (như bạn thường cần cho kiểu mã này), những điều xấu sẽ xảy ra.

Vì vậy, cho rằng, bạn có thể làm cho công việc ví dụ như sau:

template <class T> 
class IDoSomething { 
public: 
    virtual void DoSomething(T value) = 0; 
}; 

template <class T> 
class Base : public virtual IDoSomething<T> 
{ 
public: 
    virtual void DoSomething(T value) 
    { 
     // Do something here 
    } 
}; 

class IDoubleDoSomething : public virtual IDoSomething<double> 
{ 
}; 

class Foo : public virtual Base<double>, public virtual IDoubleDoSomething 
{ 
}; 
+0

Điều này dường như là gần nhất với những gì tôi đã cố gắng để đạt được và, kể từ khi tôi sở hữu 'Base', nó hoạt động tốt cho kịch bản của tôi. Nếu tôi không phải là chủ sở hữu của 'Base' thì tôi đoán các giải pháp khác ở đây sẽ được áp dụng nhiều hơn. – Clueless

2

Xem xét c sau ++ mã

class Foo 
{ 
public: 
    void DoSomething1(){} 
}; 

template<typename t> 
void MethodExpectsDosomething1(t f) 
{ 
    f.DoSomething1(); 
} 

template<typename t> 
void MethodExpectsDosomething2(t f) 
{ 
    f.DoSomething2(); 
} 

int main() 
{ 
    Foo f; 
    MethodExpectsDosomething1<Foo>(f); 

    MethodExpectsDosomething2<Foo>(f); 

    return 0; 
} 

Trong C++, bạn có thể sử dụng Foo mà không có nó thực hiện một IDoSomething1IDoSomething2. Phương thức thứ hai MethodExpectsDosomething2 sẽ không biên dịch đơn giản là Foo không có phương thức DoSomething2.

Trong C# cấu trúc như vậy là không thể và buộc bạn phải có giao diện IDoSomething1IDoSomething2 và chỉ định đó là type constraint.

Vì vậy, có thể bạn cần xem mã của mình và xem liệu có cần phải có giao diện như vậy không?

+0

vấn đề với cách gõ vịt là nó không phải là tự ghi chép tài liệu, trong khi giải pháp liên quan đến đa hình IS tự ghi lại tài liệu – akappa

+0

. xấu ... C# 4.0 sẽ không giới thiệu nó như là từ khóa 'dynamic' Chỉ vì bạn được đưa ra một cái rìu không có nghĩa là bạn phải cắt đôi chân của bạn –

+0

tôi có thể nhắc bạn rằng đối số" nó phổ biến, vì vậy nó không thể bất kỳ xấu "không phải là một tốt? Nếu bạn là người sử dụng của một chức năng templatized, làm thế nào để bạn tìm ra loại chức năng có chức năng yêu cầu từ đối tượng của bạn, và với ngữ nghĩa? Vấn đề này không tồn tại với đa hình năng động, – akappa

-1

Cung cấp mọi thứ khác theo thứ tự, thao tác này sẽ hoạt động.

class Foo : public Base<double> 
{ 
}; 

Trong mã ban đầu của bạn Foo sẽ có 2 void Phương thức DoSomething (double value) (1 là abstract). Đó sẽ là một phần cơ sở và một phần IDoubleDoSomething. Foo :: Base và Foo :: IDoubleDoSomething. Bạn có thể thử bằng cách tạm thời cấp và triển khai cho IDoubleDoSomething.DoSomething().

Nhưng kể từ Foo đã "là một" cơ sở" Bạn có những gì bạn cần mà không IDoubleDoSomething

+2

Lý do sử dụng giao diện là chuyển con trỏ đến giao diện mà không yêu cầu định nghĩa của lớp cơ sở hoặc lớp dẫn xuất. Loại bỏ các lớp giao diện là đi ngược. –

2

Trong C++, chức năng ảo tinh khiết luôn luôn phải được ghi đè trong một lớp học có nguồn gốc;. Họ có thể không kế thừa ghi đè từ cơ sở khác Nếu bạn cần đa hình năng động, tôi không nghĩ có bất kỳ sự thay thế hợp lý nào để viết một hàm trong Foo gọi hàm Base. Lưu ý rằng hàm Base không cần phải là ảo.

Tùy thuộc vào cách bạn đang sử dụng các lớp này (đặc biệt là loại thực của mỗi giao diện được biết tại thời gian biên dịch), bạn có thể sử dụng tính đa hình tĩnh để chèn lớp triển khai cụ thể của bạn vào người dùng dưới dạng thông số mẫu; ví dụ:

// No explicit interface specification with static polymorphism 
class Foo : public Base<double> 
{ 
    // Inherits DoSomething(double) 
}; 

// Template can used with any class that impelements a 
// "DoSomething" function that can accept a double. 
template <class DoubleDoSomething> 
void DoSomethingWithDouble(DoubleDoSomething & interface, double value) 
{ 
    interface.DoSomething(value); 
} 

// This particular specialisation uses a "Foo" object. 
Foo foo; 
DoSomethingWithDouble(foo, 42); 
2

Có điều là, Base::DoSomethingIWhatever::DoSomething hai chức năng không liên quan (thậm chí nếu không có bất kỳ chức năng ảo trong sạch này, bạn sẽ không thể gọi DoSomething trên một đối tượng Foo, dù sao). Base cần kế thừa từ IWhatever để tính năng này hoạt động.

Điều đó nói rằng, hãy tự hỏi nếu bạn thực sự cần điều này. Lập trình chung với các khuôn mẫu (và các khái niệm, giống như các giao diện - xem Boost.ConceptCheck) thường là một giải pháp tốt hơn trong C++ so với đa hình phụ kiểu thời gian chạy.

2

Bạn có thể vượt qua một tham số mẫu thứ hai để Base, các giao diện mà nó là thực hiện:

template <typename T, class Interface> 
class Base : public Interface { ... }; 

class Foo : public Base<double, IDoubleDoSomething> { ... }; 

Đối với điểm thưởng thêm bạn có thể templatise IDoubleDoSomething (do đó, nó ví dụ IDoSomething<double>), hoặc sử dụng một lớp đặc điểm để ánh xạ loại T đến giao diện có liên quan.

Các vấn đề liên quan