2013-02-20 29 views
5

Giả sử tôi có một mẫu lớp có thành viên pData, là một mảng của loại đơn vị T.Làm cách nào để lấy mẫu của mẫu lớp ra khỏi câu lệnh if? (C++)

template <class T> class X{ 
public: 
    int A; 
    int B; 
    T** pData; 
    X(int a,int b); 
    ~X();   
    void print(); //function which prints pData to screen 

}; 
template<class T>X<T>::X(int a, int b){ //constructor 
    A = a; 
    B = b; 
    pData = new T*[A]; 
    for(int i=0;i<A;i++) 
     pData[i]= new T[B]; 
    //Fill pData with something of type T 
} 
int main(){ 
    //... 
    std::cout<<"Give the primitive type of the array"<<std::endl; 
    std::cin>>type; 
    if(type=="int"){ 
     X<int> XArray(a,b); 
    } else if(type=="char"){ 
     X<char> Xarray(a,b); 
    } else { 
     std::cout<<"Not a valid primitive type!"; 
    } // can be many more if statements. 
    Xarray.print() //this doesn't work, as Xarray is out of scope. 
} 

Ví dụ Xarray được xây dựng bên trong câu lệnh if, tôi không thể sử dụng nó ở bất kỳ nơi nào khác. Tôi đã cố gắng tạo một con trỏ trước các câu lệnh if nhưng khi kiểu con trỏ không được biết tại thời điểm đó, tôi đã không thành công.

Điều gì sẽ là cách thích hợp để giải quyết vấn đề này?

+0

Không có câu trả lời "rõ ràng" nào vì C++ được nhập tĩnh. Bạn không thể nhắc người dùng cho một loại và sau đó tạo loại đó và sử dụng nó ở nơi khác - bạn phải biết loại tại thời gian biên dịch! Một kỹ thuật thường giải quyết tình huống này là "loại tẩy xoá", nhưng yêu cầu * bạn * chỉ định một số yếu tố chung mà tất cả các loại của bạn phải có và chỉ giao diện thông qua phần tử chung đó (ví dụ: "có thể in"). –

+0

Ở đây, [this] (http://stackoverflow.com/questions/1984492/runtime-determine-type-for-c) có thể giúp bạn. –

Trả lời

1

C++ là một ngôn ngữ tĩnh đánh máy, có nghĩa là bạn phải biết loại đối tượng tại thời gian biên dịch. Trong trường hợp này, bạn đang căn cứ vào kiểu của đối tượng được xây dựng trên đầu vào của người dùng, do đó, không thể biết kiểu khi chạy.

Cách phổ biến nhất để giải quyết vấn đề này là sử dụng dynamic polymorphism trong đó các hàm được gọi thông qua giao diện chung sử dụng late binding. Chúng tôi thực hiện điều này trong C++ sử dụng virtual functions. Ví dụ:

struct IPrintable { 
    virtual void print() = 0; 
}; 

template<class T> 
class X : public IPrintable { 
    // Same code as you showed above. 
}; 

int main() { 
    std::cout<<"Give the primitive type of the array"<<std::endl; 
    std::cin>>type; 

    std::unique_ptr<IPrintable> XArray; 

    if(type=="int"){ 
     XArray.reset(new X<int>(a,b)); 
    } else if(type=="char"){ 
     XArray.reset(new X<char>(a,b)); 
    } else { 
     std::cout<<"Not a valid primitive type!"; 
    } // can be many more if statements. 

    Xarray->print() // this works now! 
} 

Điều này giải quyết vấn đề phạm vi và cho phép bạn in bằng cách sử dụng loại động của biến XArray. Chức năng ảo là nước sốt bí mật làm cho điều này có thể xảy ra.

+0

Thừa kế không phải là giải pháp duy nhất có thể. Trong các ngôn ngữ khác, bạn có thể không có lựa chọn, nhưng trong C++, có rất nhiều cách để đạt được kết quả này. Tạo một lớp cơ sở và thực thi thừa kế chỉ vì hai đối tượng chia sẻ một thuộc tính chung có vẻ hơi khắc nghiệt với tôi. Chưa kể rằng điều này bằng cách nào đó buộc các loại trở thành "các lớp thực thể" và ngăn cản chúng trở thành "các lớp giá trị" (chúng không thể được sao chép dễ dàng và so sánh trở nên phức tạp hơn). – ereOn

+0

Tôi không ngụ ý rằng đó là lựa chọn duy nhất. Nhưng nó thường là lựa chọn đơn giản nhất khi các yếu tố khác không liên quan. Với câu hỏi chính xác của OP không có lý do gì để sử dụng cái gì phức tạp hơn như gọi lại hoặc khách truy cập. Ngoài ra, vấn đề về sao chép là moot cung cấp các lớp cơ sở là tất cả các giao diện trống vì bạn không phải lo lắng về việc cắt. –

+0

Xin lưu ý rằng tôi không nói rằng đó là một giải pháp tồi. Chúng ta không thể biết (OP có thể không nói cho chúng ta biết về tất cả những ràng buộc mà anh ta phải đối mặt). Khách truy cập * không * phức tạp. Nó thực sự là một trong những điều ngu ngốc nhất mà tôi từng thấy. Nó chỉ là tôi không nghĩ rằng mọi người học C++ không nên được khuyến khích để nghĩ rằng lập trình hướng đối tượng với kế thừa là giải pháp duy nhất: Tôi thấy hàng tuần hàng chục lập trình viên Java đến với C++ nghĩ rằng, và nó thực sự nhàm chán . – ereOn

4

Vấn đề ở đây là X<int>x<char> là các loại hoàn toàn không liên quan.

Thực tế là cả hai đều là kết quả của cùng một lớp templated sẽ không giúp ích gì ở đây.

Tôi có thể thấy một số giải pháp, nhưng những giải pháp này phụ thuộc vào những gì bạn thực sự cần. Ví dụ:

Ví dụ: bạn có thể làm cho các trường hợp X<> xuất phát từ một lớp cơ sở không có khuôn mẫu chung có phương thức print() (cuối cùng là ảo thuần túy). Nhưng trước khi bạn làm điều đó, hãy chắc chắn rằng nó có ý nghĩa trên một mức độ chức năng: người ta nên sử dụng thừa kế bởi vì nó có ý nghĩa, không chỉ vì những ràng buộc kỹ thuật. Và nếu bạn làm điều đó, bạn có thể sẽ muốn có một destructor ảo là tốt.

Bạn cũng có thể liên kết và lưu trữ std::function<void()> vào phương thức bạn muốn gọi, nhưng đảm bảo rằng các đối tượng vẫn còn "sống" (chúng không nằm trong mã hiện tại của bạn: cả X<int>X<char> đều bị hủy khi chúng đi ngoài phạm vi, cách trước khi bạn thực sự gọi print()).

Giải pháp cuối cùng là tạo một số loại biến thể tương thích với cả hai X<int>X<char> (boost::variant<> có thể trợ giúp tại đây). Sau đó bạn có thể viết một khách truy cập thực hiện chức năng print() cho từng loại.

Chọn giải pháp cuối cùng, nó sẽ trở thành một cái gì đó như:

typedef boost::variant<X<int>, X<char>> genericX; 

class print_visitor : public boost::static_visitor<void> 
{ 
public: 
    template <typename SomeType> 
    void operator()(const SomeType& x) const 
    { 
     // Your print implementation 
     // x is your underlying instance, either X<char> or X<int>. 
     // You may also make several non-templated overloads of 
     // this operator if you want to provide different implementations. 
    } 
}; 

int main() 
{ 
    boost::optional<genericX> my_x; 

    if (type=="int") { 
    my_x = X<int>(a,b); 
    } else if(type=="char") { 
    my_x = X<char>(a,b); 
    } 

    // This calls the appropriate print. 
    if (my_x) { 
    boost::apply_visitor(print_visitor(), *my_x) 
    } 
} 

Chúng tôi thực sự thiếu kiến ​​thức để đưa ra một câu trả lời dứt khoát: nếu lớp học của bạn là "thực thể", sau đó có lẽ bạn nên đi cho thừa kế. Nếu chúng giống như "các lớp giá trị", thì cách biến thể có thể phù hợp hơn.

1

Nếu bạn muốn làm việc với các mảng khác nhau, bất kể loại, mẫu của chúng không thể giúp bạn. Hiện tại, không có mối quan hệ chính xác giữa X<int>X<char>.

Nếu bạn muốn coi chúng là hai loại phụ của một loại phổ biến, bạn sẽ phải sử dụng thừa kế (và các biến được phân bổ động). Ví dụ, tất cả X<T> có thể kế thừa lớp cơ sở tương tự, nói Printable, và bạn có thể lưu trữ dữ liệu trong một unique_ptr<Printable>:

unique_ptr<Printable> r; 
if(type=="int"){ 
    r.reset(new X<int>(a,b)); 
} else if(type=="char"){   
    r.reset(new X<char>(a,b); 
} 
r->print(); 

Nhưng điều này có lẽ không phải là thiết kế tốt nhất.

Một giải pháp tốt hơn có thể là, thay vì cố gắng làm việc bên ngoài nếu, để di chuyển tất cả công việc bên trong if.Trong ví dụ đơn giản của bạn, điều này có thể được thực hiện bằng cách sao chép cuộc gọi để in, nhưng điều này cũng không đẹp lắm. Nhưng, đi về phía ý tưởng này, chúng ta có thể tạo một hàm mẫu mà không được công việc:

template<class T> 
void workWithType(int a, int b) 
{ 
    X<T> Xarray(a, b); 
    Xarray.print(); 
} 

//... 

if(type=="int"){ 
    workWithType<int>(a,b); 
} else if(type=="char"){ 
    workWithType<char>(a,b); 
} 
+0

Thừa kế không nhất thiết có nghĩa là anh ta sẽ phải sử dụng phân bổ động. – ereOn

1

Thay vì cố gắng để phù hợp với những mẫu vào main tôi sẽ đi theo con đường ngược lại so với phần còn lại của đề xuất ... di chuyển mã ra của main và vào riêng chức năng mà cần phải đối phó với nó (có thể templated) một loại duy nhất:

template <typename T> 
void generateAndPrint(int a, int b) { 
    X<T> x(a,b); 
    x.print(); 
} 
int main() { ... 
    if (type=="int") generateAndPrint<int>(a,b); 
    else if (type=="char") generateAndPrint<char>(a,b); 
    else ... 
} 
Các vấn đề liên quan