Làm thế nào để bạn 'phân bổ lại' trong C ++?


85

Làm cách nào tôi có thể sử dụng reallocC ++? Nó dường như bị thiếu trong ngôn ngữ - có newdeletenhưng không resize!

Tôi cần nó vì khi chương trình của tôi đọc nhiều dữ liệu hơn, tôi cần phân bổ lại bộ đệm để giữ nó. Tôi không nghĩ rằng deletenhập con trỏ cũ và nhập newmột con trỏ mới, lớn hơn, là lựa chọn phù hợp.


8
Stroustrup đã trả lời câu hỏi này từ lâu, hãy xem: www2.research.att.com/~bs/bs_faq2.html#renew (Đó là một khởi đầu tốt nếu bạn mới làm quen với C ++ cùng với Câu hỏi thường gặp về C ++ của Cline.)
dirkgently

8
Câu trả lời được tham chiếu bởi @dirkgently hiện có tại: stroustrup.com/bs_faq2.html#renew - và Câu hỏi thường gặp của Cline hiện là một phần của Câu hỏi thường gặp siêu cấp: isocpp.org/faq
maxschlepzig

Câu trả lời:


54

Sử dụng :: std :: vector!

Type* t = (Type*)malloc(sizeof(Type)*n) 
memset(t, 0, sizeof(Type)*m)

trở thành

::std::vector<Type> t(n, 0);

Sau đó

t = (Type*)realloc(t, sizeof(Type) * n2);

trở thành

t.resize(n2);

Nếu bạn muốn chuyển con trỏ vào hàm, thay vì

Foo(t)

sử dụng

Foo(&t[0])

Đây hoàn toàn là mã C ++ chính xác, vì vector là một mảng C thông minh.


1
Dòng memset không nên là dòng memset (t, 0, sizeof (T) * n);? n thay cho m?
Raphael Mayer

1
@anthom vâng. nó thực sự cần đượcType* t = static_cast<Type*>(malloc(n * sizeof *t));
Ryan Haining

2
Với C ++ 11 ngay bây giờ sẽ sử dụng t.data()thay vì&t[0]
knedlsepp

1
Làm thế nào bạn có thể xóa này sau đó?
a3mlord

@ a3mlord: Ý bạn là gì? Hãy để nó rơi ra khỏi phạm vi, và nó sẽ biến mất.
Lightness Races in Orbit

51

Tùy chọn phù hợp có thể là sử dụng một vùng chứa phù hợp với bạn, chẳng hạn như std::vector.

newdeletekhông thể thay đổi kích thước, bởi vì chúng chỉ cấp đủ bộ nhớ để chứa một đối tượng của kiểu đã cho. Kích thước của một loại nhất định sẽ không bao giờ thay đổi. Có new[]delete[]nhưng hiếm khi có lý do để sử dụng chúng.

Những gì realloctrong C có thể chỉ là một malloc, memcpyfreedù sao, mặc dù các trình quản lý bộ nhớ được phép làm điều gì đó thông minh nếu có đủ bộ nhớ trống liền kề.


6
@bodacydo: Không triển khai bộ đệm đang phát triển, chỉ cần sử dụng std::vector- nó sẽ tự động phát triển khi cần thiết và bạn có thể cấp phát trước bộ nhớ nếu muốn ( reserve()).
sharptooth

4
Sử dụng std :: vector <T>. Đó là những gì nó dành cho. Trong C ++, không có lý do gì để tự mình sử dụng new / delete / new [] / delete [], trừ khi bạn đang viết các lớp quản lý tài nguyên một cách rõ ràng.
Puppy

4
@bod: Có, nó có thể. (Nhân tiện, có thể std::string.)
fredoverflow

1
Có, nó có thể, không có vấn đề. Thậm chí std::stringcó thể làm điều đó. Nhân tiện, có khả năng việc đọc dữ liệu của bạn cũng có thể được đơn giản hóa. Bạn đang đọc dữ liệu của mình như thế nào?
Thomas

2
Có vẻ như thevector.resize(previous_size + incoming_size), theo sau bởi một memcpy(hoặc tương tự) thành &thevector[previous_size], là những gì bạn cần. Dữ liệu của vectơ được đảm bảo được lưu trữ "giống như một mảng".
Thomas

36

Thay đổi kích thước trong C ++ là một điều khó khăn vì có thể cần gọi các hàm tạo và hàm hủy.

Tôi không nghĩ rằng có lý do cơ bản tại sao trong C ++ bạn không thể có một resize[]toán tử để đi cùng new[]delete[], điều đó đã làm một cái gì đó tương tự như thế này:

newbuf = new Type[newsize];
std::copy_n(oldbuf, std::min(oldsize, newsize), newbuf);
delete[] oldbuf;
return newbuf;

Rõ ràng là oldsizesẽ được truy xuất từ ​​một vị trí bí mật, giống như nó ở trong đó delete[], và Typesẽ đến từ loại toán hạng. resize[]sẽ không thành công khi Kiểu không thể sao chép - điều này đúng, vì các đối tượng như vậy chỉ đơn giản là không thể di dời được. Cuối cùng, đoạn mã trên mặc định xây dựng các đối tượng trước khi gán chúng, điều mà bạn không muốn là hành vi thực tế.

Có thể có một cách tối ưu hóa, trong đó newsize <= oldsize, gọi các hàm hủy cho các đối tượng "quá cuối" của mảng mới được tạo và không làm gì khác. Tiêu chuẩn sẽ phải xác định xem liệu tối ưu hóa này là bắt buộc (như khi bạn resize()là vectơ), được phép nhưng không xác định, được phép nhưng phụ thuộc vào triển khai hoặc bị cấm.

Sau đó, câu hỏi mà bạn nên tự hỏi là, "việc cung cấp điều này có thực sự hữu ích không, vì nó vectorcũng làm được điều đó và được thiết kế đặc biệt để cung cấp một vùng chứa có thể thay đổi kích thước (của bộ nhớ liền kề - yêu cầu đó bị bỏ qua trong C ++ 98 cố định trong C ++ 03) phù hợp hơn mảng với các cách hoạt động của C ++? "

Tôi nghĩ câu trả lời được nhiều người cho là "không". Nếu bạn muốn thực hiện các bộ đệm có thể thay đổi kích thước theo cách C, hãy sử dụng bộ đệm malloc / free / realloccó sẵn trong C ++. Nếu bạn muốn thực hiện các bộ đệm có thể thay đổi kích thước theo cách C ++, hãy sử dụng một vectơ (hoặc deque, nếu bạn thực sự không cần bộ nhớ liền kề). Đừng cố kết hợp cả hai bằng cách sử dụng new[]cho bộ đệm thô, trừ khi bạn đang triển khai một vùng chứa giống như vectơ.


0

Đây là một ví dụ std :: move triển khai một vector đơn giản với một realloc (* 2 mỗi khi chúng ta đạt đến giới hạn). Nếu có cách nào để làm tốt hơn bản sao tôi có bên dưới, vui lòng cho tôi biết.

Biên dịch thành:

  g++ -std=c++2a -O2 -Wall -pedantic foo.cpp

Mã:

#include <iostream>
#include <algorithm>

template<class T> class MyVector {
private:
    T *data;
    size_t maxlen;
    size_t currlen;
public:
    MyVector<T> () : data (nullptr), maxlen(0), currlen(0) { }
    MyVector<T> (int maxlen) : data (new T [maxlen]), maxlen(maxlen), currlen(0) { }

    MyVector<T> (const MyVector& o) {
        std::cout << "copy ctor called" << std::endl;
        data = new T [o.maxlen];
        maxlen = o.maxlen;
        currlen = o.currlen;
        std::copy(o.data, o.data + o.maxlen, data);
    }

    MyVector<T> (const MyVector<T>&& o) {
        std::cout << "move ctor called" << std::endl;
        data = o.data;
        maxlen = o.maxlen;
        currlen = o.currlen;
    }

    void push_back (const T& i) {
        if (currlen >= maxlen) {
            maxlen *= 2;
            auto newdata = new T [maxlen];
            std::copy(data, data + currlen, newdata);
            if (data) {
                delete[] data;
            }
            data = newdata;
        }
        data[currlen++] = i;
    }

    friend std::ostream& operator<<(std::ostream &os, const MyVector<T>& o) {
        auto s = o.data;
        auto e = o.data + o.currlen;;
        while (s < e) {
            os << "[" << *s << "]";
            s++;
        }
        return os;
    }
};

int main() {
    auto c = new MyVector<int>(1);
    c->push_back(10);
    c->push_back(11);
}

-7

thử một cái gì đó như thế:

typedef struct Board
{
    string name;
    int size = 0;
};

typedef struct tagRDATA
{
    vector <Board> myBoards(255);

    // Board DataBoard[255];
    int SelectedBoard;

} RUNDATA;

Vector sẽ phàn nàn. Đó là lý do tại sao các mảng, malloc và new vẫn tồn tại.


12
Không, đó không phải là lý do tại sao. Và tôi không thấy điều này trả lời câu hỏi theo bất kỳ cách nào. Hoặc tại sao bạn đang sử dụng typedefở khắp mọi nơi như thể bạn đang viết C.
Lightness Races ở Orbit
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.