2010-06-21 37 views
11

Tôi không chắc liệu tiêu đề câu hỏi có chính xác không ... Hãy để tôi bắt đầu bằng cách giải thích kịch bản đơn giản ban đầu của tôi, và sau đó chuyển sang giải thích những gì tôi muốn làm, nhưng không thể.Tính đa hình kiểu trả về cho giá trị

Nguyên, tôi đã có cái gì đó như:

class Operand; 

Operand genOperandA() { ...; return Operand(); } 
Operand genOperandB() { ...; return Operand(); } 
... // more operand-generation functions 

typedef Operand (*OpGen)(); 

// Table of function pointers 
static const OpGen generators[] = 
{ 
    genOperandA, 
    genOperandB, 
    ... 
}; 

// Function to do some operation on the operand 
void operate(Operand& op); 

... 

// Example call 
operate(generators[1]()); 

Cho đến nay rất tốt (tôi nghĩ). Tuy nhiên, hiện có một số loại toán hạng có nguồn gốc, ví dụ: class RegisterOperand : public Operand. Tôi có các hàm mới, chuyên dụng genOperand lý tưởng sẽ trả lại các trường hợp của các kiểu có nguồn gốc. Nhưng tôi không thể làm điều này:

Operand genOperandC() { ...; return RegisterOperand(); } 

và tôi không thể làm điều này:

RegisterOperand genOperandC() { ...; return RegisterOperand(); } 

static const OpGen generators[] = 
{ 
    ... 
    genOperandC, 
}; 

Tuy nhiên, tôi biết điều này sẽ làm việc nếu tôi được trở lại tham chiếu hoặc con trỏ loại, vì vậy chỉ tùy chọn Tôi hiện có là một cái gì đó như:

Operand *genOperandC() { ...; return new RegisterOperand(); } 

hiện yêu cầu dọn dẹp rõ ràng không cần thiết ban đầu.

Bất kỳ lựa chọn thay thế nào mà tôi chưa xem xét?

Trả lời

4

Có thể có các thiết kế khác không yêu cầu bạn sử dụng con trỏ, nhưng nếu bạn cần hoặc muốn đi theo cách này, điều này có thể khiến bạn quan tâm.


Nếu trả về con trỏ là một vấn đề (vì cần phải "dọn dẹp"), bạn nên cân nhắc sử dụng con trỏ thông minh làm kiểu trả về.

Dưới đây là một ví dụ về phương pháp nhà máy của bạn với con trỏ thông minh:

boost::shared_ptr<Operand> genOperandC() 
{ 
    return boost::shared_ptr<Operand>(new RegisterOperand()); 
} 

Bằng cách này, bạn sẽ không phải gọi delete bằng tay: nó sẽ được thực hiện bởi các destructor của boost::shared_ptr<Operand> cho bạn khi cần thiết.

Nếu sau đó bạn cần phải cast con trỏ kết quả, boost cung cấp đúc chức năng cũng như:

boost::shared_ptr<Operand> op = genOperandC(); 

boost::shared_ptr<RegisterOperand> rop = 
    boost::dynamic_pointer_cast<RegisterOperand>(op); 
+1

Sử dụng 'boost :: make_shared' thay vì tự tạo con trỏ được chia sẻ, đó là phương thức trình bao đơn giản nhưng giảm số lượng phân bổ bộ nhớ cần thiết (và do đó nhanh hơn) –

7

Bạn có thể quấn:

class Operand 
{ 
public: 

private: 
    std::unique_ptr<OperandImpl> mImpl; 
}; 

này tương tự như một mẫu chiến lược: các toán hạng thực tế hành vi bị ẩn và có thể truy cập thông qua Giao diện không phải ảo. Người dùng nhận được một bản sao của Operand, cô ấy không cần phải biết bất cứ điều gì về nội bộ của nó và có thể sử dụng nó, và bạn được tự do thực hiện các hành vi có nguồn gốc khác nhau.

+0

Ý tưởng ở đây là các kiểu con khác nhau của tôi thực sự được kế thừa từ 'OperandImpl', và thành viên đó gọi hàm' Operand' chỉ nên được ủy quyền thành '* mImpl'? –

+0

Vâng, đây là một Mẫu chiến lược cổ điển, nơi xử lý bộ nhớ thực tế bị ẩn khỏi máy khách (thay vì trả về con trỏ, thông minh hay không, thành 'OperandImpl'). Tuy nhiên, bạn có thể thực hiện một số công việc trước khi ủy nhiệm (kiểm tra đối số, ghi nhật ký ...) –

0

Làm thế nào về một cái gì đó như thế này?

Lưu ý rằng bạn không thể thực hiện thao tác đơn giản (máy phát [i]()) làm vận hành ban đầu() lấy tham chiếu không phải const.

#include <iostream> 
#include <string> 
#include <memory> 

class Operand { 
public: 
     Operand(std::string x = "Operand"): x(x) {} 
     const std::string x; 
}; 

class RegisterOperand: public Operand { 
public: 
     RegisterOperand(std::string x = "RegisterOperand") 
       : Operand(x) {} 
}; 

typedef std::auto_ptr<Operand> POperand; 

POperand genOperandA() { return POperand(new Operand("genOperandA")); } 
POperand genOperandB() { return POperand(new Operand("genOperandB")); } 
POperand genOperandC() { return POperand(new RegisterOperand()); } 
// more operand-generation functions 

typedef POperand (*OpGen)(); 

// Table of function pointers 
static const OpGen generators[] = 
{ 
     genOperandA, 
     genOperandB, 
     genOperandC, 
}; 

void operate(const POperand& op) 
{ 
     std::cout << op->x << std::endl; 
} 

int main() 
{ 
     operate(generators[0]()); 
     operate(generators[1]()); 
     operate(generators[2]()); 
     return 0; 
} 
+0

Việc sử dụng 'auto_ptr' không được chấp nhận trong C++ 0x. Thay vào đó, hãy sử dụng 'unique_ptr'. Nếu bạn chưa có quyền truy cập vào C++ 0x, hãy chọn 'shared_ptr'. –

+1

@Matthieu: Trong khi tôi thích unique_ptr rất nhiều, tôi sẽ do dự khi sử dụng nó vì nó dựa trên một tính năng ngôn ngữ C++ 0x. Tôi nghĩ rằng đó là okay để sử dụng std :: auto_ptr nếu bạn biết những gì bạn đang làm và không muốn các chi phí tham khảo-đếm của shared_ptr. Chỉ cần 2 xu của tôi. – sellibitze

0

Tôi biết câu hỏi này đã được hỏi một thời gian trước nhưng gần đây tôi đã gặp phải vấn đề này và tôi đã đưa ra một giải pháp khác mà tôi có thể hữu ích ở đây.

Vì vậy, ý tưởng là tạo trình bao bọc để quản lý con trỏ nhưng cũng hỗ trợ sao chép trình bao bọc không giống như con trỏ duy nhất chỉ có thể di chuyển.

class PolymorphicType { 
public: 
    /* 
     Class interface ... 
    */ 
    PolymorphicType() { it = nullptr; } 
    virtual ~PolymorphicType() { if(it != nullptr) delete it; }   

    PolymorphicType& operator=(const PolymorphicType& org) { 
      //Clone the derived type and store it 
      if(org.it != nullptr) 
       it = org.it->clone(); 
    } 
private: 
    Base* it; 
}; 

Mỗi lớp dẫn xuất bây giờ phải triển khai phương pháp sao chép riêng và bạn tốt! Và chỉ trong trường hợp đây là một bài tốt đẹp giải thích cách nhân bản các loại có nguồn gốc hoạt động: Copying derived entities using only base class pointers, (without exhaustive testing!) - C++

Hy vọng điều này sẽ giúp một người nào đó ra!

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