2010-05-11 30 views
6

Tôi có một cơ sở mã lớn, ban đầu C được chuyển đến C++ nhiều năm trước, hoạt động trên một số mảng lớn dữ liệu không gian. Các mảng này chứa các cấu trúc biểu diễn các thực thể điểm và tam giác đại diện cho các mô hình bề mặt. Tôi cần phải cấu trúc lại mã như vậy theo cách cụ thể mà các thực thể này được lưu trữ nội bộ khác nhau cho các tình huống cụ thể. Ví dụ, nếu các điểm nằm trên một lưới phẳng thông thường, tôi không cần phải lưu trữ các tọa độ X và Y, vì chúng có thể được tính toán trên bay, như các hình tam giác. Tương tự, tôi muốn tận dụng lợi thế của các công cụ cốt lõi như STXXL để lưu trữ. Cách đơn giản nhất để thực hiện việc này là thay thế truy cập mảng bằng các hàm kiểu put và get, ví dụ:Tôi có thể sử dụng toán tử [] trong C++ để tạo mảng ảo

point[i].x = XV; 

trở thành

Point p = GetPoint(i); 
p.x = XV; 
PutPoint(i,p); 

Như bạn có thể tưởng tượng, đây là một cấu trúc lại rất tẻ nhạt trên một cơ sở mã lớn, dễ bị tất cả các loại lỗi trên đường. Những gì tôi muốn làm là viết một lớp bắt chước mảng bằng cách nạp chồng toán tử []. Khi các mảng đã tồn tại trên heap và di chuyển xung quanh với reallocs, mã đã giả định rằng tham chiếu vào mảng chẳng hạn như

point *p = point + i; 

không được sử dụng. Lớp này có khả thi để viết không? Ví dụ, viết các phương thức dưới đây về toán tử [];

void MyClass::PutPoint(int Index, Point p) 
{ 
    if (m_StorageStrategy == RegularGrid) 
    { 
     int xoffs,yoffs; 
     ComputeGridFromIndex(Index,xoffs,yoffs); 
     StoreGridPoint(xoffs,yoffs,p.z); 
    } else 
     m_PointArray[Index] = p; 
    } 
} 

Point MyClass::GetPoint(int Index) 
{ 
    if (m_StorageStrategy == RegularGrid) 
    { 
     int xoffs,yoffs; 
     ComputeGridFromIndex(Index,xoffs,yoffs); 
     return GetGridPoint(xoffs,yoffs); // GetGridPoint returns Point 
    } else 
     return m_PointArray[Index]; 
    } 
} 

Mối quan tâm của tôi là tất cả các lớp mảng tôi đã xem có xu hướng chuyển qua tham chiếu, trong khi tôi nghĩ tôi phải chuyển cấu trúc theo giá trị. Tôi nghĩ rằng nó nên làm việc đặt khác hơn là hiệu suất, bất cứ ai có thể nhìn thấy bất kỳ cạm bẫy lớn với cách tiếp cận này. n.b. lý do tôi phải chuyển qua giá trị là để có được

point[a].z = point[b].z + point[c].z 

để hoạt động chính xác nơi loại lưu trữ cơ bản thay đổi.

Trả lời

0

Sau khi đọc câu trả lời ở trên, tôi quyết định rằng câu trả lời của Pete với hai phiên bản của operator[] là cách tốt nhất về phía trước. Để xử lý việc biến đổi giữa các kiểu tại thời gian chạy, tôi đã tạo ra một lớp mẫu mảng mới lấy bốn tham số như sau;

template<class TYPE, class ARG_TYPE,class BASE_TYPE, class BASE_ARG_TYPE> 
class CMorphArray 
{ 
int GetSize() { return m_BaseData.GetSize(); } 
BOOL IsEmpty() { return m_BaseData.IsEmpty(); } 

// Accessing elements 
const TYPE& GetAt(int nIndex) const; 
TYPE& GetAt(int nIndex); 
void SetAt(int nIndex, ARG_TYPE newElement); 
const TYPE& ElementAt(int nIndex) const; 
TYPE& ElementAt(int nIndex); 

// Potentially growing the array 
int Add(ARG_TYPE newElement); 

// overloaded operator helpers 
const TYPE& operator[](int nIndex) const; 
TYPE& operator[](int nIndex); 

    CBigArray<BASE_TYPE, BASE_ARG_TYPE> m_BaseData; 
private: 
    CBigArray<TYPE, ARG_TYPE> m_RefCache; 
    CBigArray<int, int&> m_RefIndex; 
    CBigArray<int, int&> m_CacheIndex; 

    virtual void Convert(BASE_TYPE,ARG_TYPE) = 0; 
    virtual void Convert(TYPE,BASE_ARG_TYPE) = 0; 

    void InitCache(); 
    TYPE& GetCachedElement(int nIndex); 
}; 

Lưu trữ dữ liệu chính ở dạng m_BaseData là dữ liệu ở định dạng gốc, có thể thay đổi theo loại được thảo luận. m_RefCache là mảng thứ cấp cho bộ nhớ đệm của các phần tử theo định dạng mong muốn và hàm GetCachedElement sử dụng các hàm Convert ảo để dịch dữ liệu khi nó được chuyển vào và ra khỏi bộ nhớ cache. Bộ nhớ cache cần phải lớn nhất là số lượng tham chiếu đồng thời có thể hoạt động cùng một lúc, nhưng trong trường hợp của tôi có thể sẽ được hưởng lợi từ việc lớn hơn vì nó làm giảm số lượng chuyển đổi cần thiết. Trong khi việc triển khai con trỏ của Alsk có thể sẽ hoạt động tốt, giải pháp đưa ra yêu cầu ít bản sao đối tượng và biến tạm thời hơn, và phải đủ khả năng thực hiện tốt hơn một chút, điều quan trọng trong trường hợp này.

Xin lỗi cho tất cả người hâm mộ STL về giao diện và cảm biến MFC cũ hơn; phần còn lại của dự án là MFC nên nó có ý nghĩa hơn trong trường hợp này. CBigArray là kết quả của một related stack overflow question đã trở thành cơ sở xử lý mảng lớn của tôi. Tôi hy vọng sẽ hoàn thành việc thực hiện ngày hôm nay và kiểm tra vào ngày mai. Nếu tất cả đi vào bụng tôi, tôi sẽ chỉnh sửa bài đăng này một cách accoringly.

5

Bạn không cần phải chuyển mảng theo giá trị. Để thay đổi các giá trị trong mảng, bạn muốn có hai phiên bản operator[], một phiên bản trả về tham chiếu (để thay đổi) và một tham chiếu const.

Không có lý do gì về nguyên tắc không sử dụng operator[], miễn là bạn không cần thay đổi loại lưu trữ khi chạy - không có toán tử ảo, vì vậy bạn sẽ cần hàm được đặt tên nếu bạn muốn chạy đa hình. Trong trường hợp đó, bạn có thể tạo một đơn giản struct điều chỉnh các cuộc gọi của nhà điều hành đến các cuộc gọi hàm (mặc dù nó phụ thuộc vào API lưu trữ - nếu mã giả định rằng gán biến thành viên của điểm thay đổi dữ liệu được lưu trữ, bạn có thể phải thực hiện điểm gõ một biến mẫu quá để điều này có thể được ghi đè).

Nhìn mã mẫu của bạn, nó có một thử nghiệm cho chiến lược lưu trữ. Đừng làm điều này. Hoặc sử dụng OO và có đối tượng lưu trữ của bạn thực hiện một giao diện ảo chung, hoặc (có lẽ tốt hơn) sử dụng lập trình mẫu để thay đổi cơ chế lưu trữ.

Nếu bạn nhìn vào các đảm bảo được thực hiện bởi std::vector (trong các tiêu chuẩn C++ gần đây hơn), thì có thể có thứ gì đó có lưu trữ động và cho phép sử dụng số học con trỏ, mặc dù yêu cầu lưu trữ liền kề. Do một số giá trị của bạn được tạo khi đang bay, có lẽ không đáng để đặt hạn chế đó vào việc triển khai của bạn, nhưng bản thân ràng buộc không ngăn cản việc sử dụng operator[].

+0

Trả lại tham chiếu đến điểm ngụ ý có nhiều điểm duy nhất cho tất cả các tham chiếu có khả năng hoạt động cùng một lúc. Điều này sẽ yêu cầu một bộ nhớ cache của dữ liệu chuyển đổi, nhưng phải được hoàn toàn khả thi. Cảm ơn câu trả lời. –

+0

WRT sử dụng chiến lược lưu trữ thay vì OO hoặc mẫu, nó sẽ không hoạt động vì mã hiện tại không được viết để hỗ trợ nó và yêu cầu chính là giảm thiểu số lượng viết lại. Tôi muốn thả vào một cái gì đó là chạy đa thời gian ở cuối phía sau, nhưng ở mặt trước bắt chước một mảng C. Nếu tôi đã viết nó từ đầu, mẫu chắc chắn là con đường để đi. –

2

Điều bạn muốn là có thể, nhưng khi bạn cần quyền truy cập viết, kết quả sẽ đôi khi phức tạp hơn một chút.Những gì bạn muốn là chức năng setter trả về không phải là một "điểm ghi truy cập" trực tiếp, chứ không phải là một bản sao tạm thời, mà sẽ làm việc viết một khi bản sao đi ra khỏi phạm vi.

Tiếp theo đoạn mã cố gắng phác thảo các giải pháp:

class PointVector 
{ 
    MyClass container_; 

    public: 
    class PointExSet: public Point 
    { 
    MyClass &container_; 
    int index_; 

    public: 
    PointExSet(MyClass &container, int index) 
     :Point(container.GetVector(index)),container_(container),index_(index) 
    { 
    } 

    ~PointExSet() 
    { 
     container_.PutVector(index_) = *this; 
    } 
    }; 

    PointExSet operator [] (int i) 
    { 
    return PointExSet(container_,i); 
    } 
}; 

Nó không phải là tốt đẹp như bạn có lẽ sẽ hy vọng nó được, nhưng tôi sợ bạn không thể có được một giải pháp tốt hơn nhiều trong C++.

+0

Yup, do đó cần tham khảo. Tôi nghĩ rằng tôi sẽ có thể làm điều đó bằng cách sử dụng một bộ nhớ cache của các loại chuyển đổi, với một chỉ số cùng kích thước như mảng được sử dụng để duy trì bộ nhớ cache. –

1

Để có toàn quyền kiểm soát hoạt động trên mảng, toán tử [] phải trả về một đối tượng đặc biệt (được phát minh từ lâu và gọi là "con trỏ") sẽ xử lý các thao tác cho bạn. Như một ví dụ:

class Container 
{ 
    PointCursor operator [] (int i) 
    { 
    return PointCursor(this,i); 
    } 
}; 
class PointCursor 
{ 
public: 
    PointCursor(_container, _i) 
     : container(_container), i(_i), 
     //initialize subcursor 
     x(container, i) {}  

    //subcursor 
    XCursor x; 
private: 
    Container* container; 
    int i; 
}; 
class XCursor 
{ 
public: 
    XCursor(_container, _i) 
     : container(_container), i(_i) {} 

    XCursor& operator = (const XCursor& xc) 
    { 
      container[i].x = xc.container[xc.i].x; 
      //or do whatever you want over x 
    } 

    Container* container; 
    int i; 
} 
//usage 
my_container[i].x = their_container[j].x; //calls XCursor::operator =() 
Các vấn đề liên quan