2008-09-10 36 views
15

Tôi có một lớp học để phân tích một ma trận mà giữ kết quả trong một thành viên mảng:Làm thế nào để đối phó với mảng (được khai báo trên ngăn xếp) trong C++?

class Parser 
{ 
    ... 
    double matrix_[4][4]; 
}; 

Người dùng của lớp này cần gọi một hàm API (như trong, một chức năng tôi không kiểm soát được, vì vậy Tôi không thể thay đổi giao diện của nó để làm cho mọi thứ hoạt động dễ dàng hơn) trông giống như sau:

void api_func(const double matrix[4][4]); 

Cách duy nhất tôi đã đưa ra cho người gọi để chuyển mảng kết quả đến hàm bằng cách tạo thành viên công khai:

void myfunc() 
{ 
    Parser parser; 
    ... 
    api_func(parser.matrix_); 
} 

Đây có phải là cách duy nhất để làm việc không? Tôi kinh ngạc bởi cách mảng đa chiều không linh hoạt được khai báo như thế này. Tôi nghĩ rằng matrix_ về cơ bản sẽ giống như double** và tôi có thể truyền (an toàn) giữa hai thiết bị. Khi nó quay ra, tôi thậm chí không thể tìm thấy không an toàn cách để phân phát giữa mọi thứ. Nói rằng tôi thêm một accessor đến lớp Parser:

void* Parser::getMatrix() 
{ 
    return (void*)matrix_; 
} 

này sẽ biên dịch, nhưng tôi không thể sử dụng nó, vì có vẻ không phải là một cách để cast lại kiểu mảng lập dị:

// A smorgasbord of syntax errors... 
    api_func((double[][])parser.getMatrix()); 
    api_func((double[4][4])parser.getMatrix()); 
    api_func((double**)parser.getMatrix()); // cast works but it's to the wrong type 

lỗi này là:

lỗi C2440: 'loại dàn diễn viên': không thể chuyển đổi từ 'void *' thành 'const double [4] [4]

... với một phụ lục hấp dẫn:

Không có chuyển đổi các loại mảng, mặc dù có chuyển đổi tài liệu tham khảo hoặc gợi ý cho các mảng

tôi không thể xác định làm thế nào để đúc để một tham chiếu hoặc con trỏ đến mảng hoặc, mặc dù nó có thể sẽ không giúp tôi ở đây. Để chắc chắn, vào thời điểm này vấn đề hoàn toàn là học thuật, vì các phôi void* khó có thể sạch hơn một thành viên lớp duy nhất được công khai!

Trả lời

16

Dưới đây là một cách sạch đẹp:

class Parser 
{ 
public: 
    typedef double matrix[4][4]; 

    // ... 

    const matrix& getMatrix() const 
    { 
     return matrix_; 
    } 

    // ... 

private: 
    matrix matrix_; 
}; 

Bây giờ bạn đang làm việc với một loại tên mô tả chứ không phải là một mảng, nhưng vì đó là một typedef trình biên dịch sẽ vẫn cho phép đi qua nó để các hàm API không thể thay đổi có loại cơ sở.

+0

Đánh bại tôi. :-) – Andrew

+0

vì tôi đã sử dụng tên loại ngắn hơn. Hãy suy nghĩ của tất cả các thời gian bạn lãng phí gõ thêm năm ký tự ... ;-p – Shog9

+0

Và ông mở rộng "// ..." s là tốt ... Tuy nhiên tôi cảm thấy tôi nên chấp nhận đầu tiên. Xin lỗi Andrew! – Owen

4

Tôi đã sử dụng một sự kết hợp như thế này để vượt qua xung quanh ma trận trong quá khứ:

union matrix { 
    double dflat[16]; 
    double dmatr[4][4]; 
}; 

Sau đó, vượt qua một con trỏ để setter của bạn và sao chép dữ liệu vào ma trận trong lớp học của bạn.

Có nhiều cách để xử lý cách này (nói chung là chung hơn), nhưng giải pháp này có xu hướng trở nên sạch sẽ nhất, theo kinh nghiệm của tôi.

6

Hãy thử điều này.Nó biên dịch sạch trên gcc 4.1.3:

typedef double FourSquare[4][4]; 

class Parser 
{ 
    private: 
    double matrix_[4][4]; 

    public: 
    Parser() 
    { 
     for(int i=0; i<4; i++) 
      for(int j=0; j<4; j++) 
      matrix_[i][j] = i*j; 
    } 

    public: 
    const FourSquare& GetMatrix() 
    { 
     return matrix_; 
    } 
}; 

void api_func(const double matrix[4][4]) 
{ 
} 

int main(int argc, char** argv) 
{ 
    Parser parser; 
    api_func(parser.GetMatrix()); 
    return 0; 
} 
4

Tôi nghĩ matrix_ về cơ bản sẽ giống như một đôi **

Trong C có đúng mảng đa chiều, không mảng của con trỏ đến mảng, do đó, một đôi [4] [4] là một mảng liền kề của bốn mảng [4], tương đương với một đôi [16], không phải là một (đôi *) [4].

Không có chuyển đổi các loại mảng, mặc dù có chuyển đổi tài liệu tham khảo hoặc gợi ý cho các mảng Đúc một giá trị cho một đôi [4] [4] sẽ cố gắng để xây dựng một trên stack - tương đương với std: : string (parser.getMatrix()) - ngoại trừ mảng không cung cấp một hàm tạo phù hợp. Bạn có thể không muốn làm điều đó, ngay cả khi bạn có thể.

Vì loại mã hóa sải chân, bạn cần một loại đầy đủ (gấp đôi [] [] sẽ không thực hiện). Bạn có thể diễn giải lại các void * để ((double [4] [4]) *), và sau đó tham khảo. Nhưng đó là dễ dàng nhất để typedef ma trận và trả về một tham chiếu đúng loại ở nơi đầu tiên:

typedef double matrix_t[4][4]; 

class Parser 
{ 
    double matrix_[4][4]; 
public: 
    void* get_matrix() { return static_cast<void*>(matrix_); } 

    const matrix_t& get_matrix_ref() const { return matrix_; } 
}; 

int main() 
{ 
    Parser p; 

    matrix_t& data1 = *reinterpret_cast<matrix_t*>(p.get_matrix()); 

    const matrix_t& data2 = p.get_matrix_ref(); 
} 
2

Để xây dựng trên câu trả lời được lựa chọn, quan sát dòng này

const matrix& getMatrix() const 

này là rất tốt, bạn don không phải lo lắng về con trỏ và đúc. Bạn đang trả lại tham chiếu cho đối tượng ma trận cơ bản. IMHO tài liệu tham khảo là một trong những tính năng tốt nhất của C++, mà tôi bỏ lỡ khi mã hóa trong thẳng C.

Nếu bạn không quen thuộc với sự khác biệt giữa các tài liệu tham khảo và con trỏ trong C++, read this

Dù sao đi nữa, bạn phải lưu ý rằng nếu đối tượng Parser thực sự sở hữu đối tượng ma trận cơ bản nằm ngoài phạm vi, bất kỳ mã nào cố truy cập ma trận thông qua tham chiếu đó sẽ tham chiếu đến đối tượng nằm ngoài phạm vi và bạn sẽ gặp sự cố .

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