khởi tạo mảng const trong trình khởi tạo lớp trong C ++


77

Tôi có lớp sau trong C ++:

class a {
    const int b[2];
    // other stuff follows

    // and here's the constructor
    a(void);
}

Câu hỏi đặt ra là, làm cách nào để khởi tạo b trong danh sách khởi tạo, vì tôi không thể khởi tạo nó bên trong phần thân của hàm của hàm tạo, bởi vì b là const?

Điều này không hoạt động:

a::a(void) : 
    b([2,3])
{
     // other initialization stuff
}

Chỉnh sửa: Trường hợp điển hình là khi tôi có thể có các giá trị bkhác nhau cho các trường hợp khác nhau, nhưng các giá trị được biết là không đổi trong thời gian tồn tại của trường hợp đó.

Câu trả lời:


35

Giống như những người khác đã nói, ISO C ++ không hỗ trợ điều đó. Nhưng bạn có thể giải quyết nó. Chỉ cần sử dụng std :: vector thay thế.

int* a = new int[N];
// fill a

class C {
  const std::vector<int> v;
public:
  C():v(a, a+N) {}
};

12
Vấn đề với điều này là nó sử dụng vectơ gây ra chi phí bổ sung.
vy32 23/12/10

1
Vấn đề không phải là nó sử dụng vectơ, hoặc một loại bộ nhớ so với một loại bộ nhớ khác. Đó là bạn không thể khởi tạo trực tiếp vector với một bộ giá trị tùy ý. @ CharlesB's techinique sẽ hoạt động với boost hoặc std để thực hiện điều đó trong hai bước.
Rick Berge,

1
Bạn luôn có thể sử dụng một std::arrayđể tránh một số chi phí đó.
bremen_matt

81

Với C ++ 11, câu trả lời cho câu hỏi này giờ đã thay đổi và trên thực tế bạn có thể làm:

struct a {
    const int b[2];
    // other bits follow

    // and here's the constructor
    a();
};

a::a() :
    b{2,3}
{
     // other constructor work
}

int main() {
 a a;
}

25

Nó là không thể trong tiêu chuẩn hiện hành. Tôi tin rằng bạn sẽ có thể làm điều này trong C ++ 0x bằng cách sử dụng danh sách trình khởi tạo (xem Sơ lược về C ++ 0x , của Bjarne Stroustrup, để biết thêm thông tin về danh sách trình khởi tạo và các tính năng C ++ 0x thú vị khác).


12

std::vectorsử dụng đống. Geez, thật là lãng phí nếu chỉ để constkiểm tra sự tỉnh táo. Vấn đề std::vectorlà tăng trưởng năng động tại thời điểm chạy, không phải bất kỳ kiểm tra cú pháp cũ nào nên được thực hiện tại thời điểm biên dịch. Nếu bạn không phát triển thì hãy tạo một lớp để bọc một mảng bình thường.

#include <stdio.h>


template <class Type, size_t MaxLength>
class ConstFixedSizeArrayFiller {
private:
    size_t length;

public:
    ConstFixedSizeArrayFiller() : length(0) {
    }

    virtual ~ConstFixedSizeArrayFiller() {
    }

    virtual void Fill(Type *array) = 0;

protected:
    void add_element(Type *array, const Type & element)
    {
        if(length >= MaxLength) {
            // todo: throw more appropriate out-of-bounds exception
            throw 0;
        }
        array[length] = element;
        length++;
    }
};


template <class Type, size_t Length>
class ConstFixedSizeArray {
private:
    Type array[Length];

public:
    explicit ConstFixedSizeArray(
        ConstFixedSizeArrayFiller<Type, Length> & filler
    ) {
        filler.Fill(array);
    }

    const Type *Array() const {
        return array;
    }

    size_t ArrayLength() const {
        return Length;
    }
};


class a {
private:
    class b_filler : public ConstFixedSizeArrayFiller<int, 2> {
    public:
        virtual ~b_filler() {
        }

        virtual void Fill(int *array) {
            add_element(array, 87);
            add_element(array, 96);
        }
    };

    const ConstFixedSizeArray<int, 2> b;

public:
    a(void) : b(b_filler()) {
    }

    void print_items() {
        size_t i;
        for(i = 0; i < b.ArrayLength(); i++)
        {
            printf("%d\n", b.Array()[i]);
        }
    }
};


int main()
{
    a x;
    x.print_items();
    return 0;
}

ConstFixedSizeArrayFillerConstFixedSizeArray có thể tái sử dụng.

Đầu tiên cho phép kiểm tra giới hạn thời gian chạy trong khi khởi tạo mảng (giống như vectơ có thể), sau này có thể trở thành const sau khi khởi tạo này.

Thứ hai cho phép mảng được phân bổ bên trong một đối tượng khác, có thể nằm trên heap hoặc đơn giản là ngăn xếp nếu đó là vị trí của đối tượng. Không lãng phí thời gian để phân bổ từ đống. Nó cũng thực hiện kiểm tra const thời gian biên dịch trên mảng.

b_fillerlà một lớp private nhỏ để cung cấp các giá trị khởi tạo. Kích thước của mảng được kiểm tra tại thời điểm biên dịch với các đối số mẫu, vì vậy không có cơ hội vượt quá giới hạn.

Tôi chắc rằng có nhiều cách kỳ lạ hơn để sửa đổi điều này. Đây là một cú đâm ban đầu. Tôi nghĩ rằng bạn có thể bù đắp cho bất kỳ thiếu sót nào của trình biên dịch với các lớp.


3
Có vấn đề gì khi nó nằm trên đống? Bộ nhớ sẽ được sử dụng trong suốt vòng đời của đối tượng cho dù nó nằm trên heap hay stack. Xem xét rằng nhiều kiến ​​trúc có đống và ngăn xếp ở hai phía đối diện của cùng một đoạn bộ nhớ, do đó về mặt lý thuyết chúng có thể gặp nhau ở giữa, tại sao đối tượng đó lại nằm ở đâu?
Nathan Fellman

2
@Nathan Fellman: Đây có thể được coi là tối ưu hóa quá mức, nhưng trong một số trường hợp, bạn muốn đối tượng của mình thực hiện phân bổ bằng 0 (để sử dụng trên ngăn xếp). Trong trường hợp đó, a newlà quá nhiều và thậm chí nhiều hơn nếu bạn biết tại thời điểm biên dịch bạn cần bao nhiêu. Ví dụ, một số triển khai của std :: vector phân bổ các mục của nó trong bộ đệm bên trong, thay vì sử dụng new, làm cho các vectơ nhỏ khá rẻ để xây dựng / phá hủy.
paercebal

Đôi khi, trình biên dịch sẽ tối ưu hóa đủ điều đó std::vectorvà các mảng mang lại cùng một mã chính xác. Hừ!
Sebastian Mach

9

Tiêu chuẩn ISO C ++ không cho phép bạn làm điều này. Nếu có, cú pháp có thể sẽ là:

a::a(void) :
b({2,3})
{
    // other initialization stuff
}

Hoặc một cái gì đó dọc theo các đường dây. Từ câu hỏi của bạn, nó thực sự có vẻ như những gì bạn muốn là một thành viên lớp hằng (hay còn gọi là tĩnh) là mảng. C ++ cho phép bạn làm điều này. Như vậy:

#include <iostream>

class A 
{
public:
    A();
    static const int a[2];
};

const int A::a[2] = {0, 1};

A::A()
{
}

int main (int argc, char * const argv[]) 
{
    std::cout << "A::a => " << A::a[0] << ", " << A::a[1] << "\n";
    return 0;
}

Đầu ra là:

A::a => 0, 1

Tất nhiên bây giờ vì đây là thành viên lớp tĩnh nên nó giống nhau đối với mọi thể hiện của lớp A. Nếu đó không phải là điều bạn muốn, tức là bạn muốn mỗi thể hiện của A có các giá trị phần tử khác nhau trong mảng a thì bạn đang thực hiện sai lầm khi cố gắng làm cho mảng bắt đầu bằng const. Bạn chỉ nên làm điều này:

#include <iostream>

class A 
{
public:
    A();
    int a[2];
};

A::A()
{
    a[0] = 9; // or some calculation
    a[1] = 10; // or some calculation
}

int main (int argc, char * const argv[]) 
{
    A v;
    std::cout << "v.a => " << v.a[0] << ", " << v.a[1] << "\n";
    return 0;
}

1
tại sao nó là một sai lầm để làm cho mảng const bắt đầu bằng? Điều gì sẽ xảy ra nếu tôi muốn các giá trị giữ nguyên trong vòng đời của phiên bản, chẳng hạn như một số loại id?
Nathan Fellman

Sau đó, bạn nên sử dụng một loại enum.
orj

1
làm thế nào tôi sẽ sử dụng một loại enum ở đây?
Nathan Fellman 20/08/08

4

Trong trường hợp tôi có một mảng không đổi, nó luôn được thực hiện dưới dạng tĩnh. Nếu bạn có thể chấp nhận điều đó, mã này sẽ được biên dịch và chạy.

#include <stdio.h>
#include <stdlib.h>

class a {
        static const int b[2];
public:
        a(void) {
                for(int i = 0; i < 2; i++) {
                        printf("b[%d] = [%d]\n", i, b[i]);
                }
        }
};

const int a::b[2] = { 4, 2 };

int main(int argc, char **argv)
{
        a foo;
        return 0;
}

1
giả sử rằng tôi thực sự muốn một thành viên tĩnh, nhưng điều đó không phải lúc nào cũng vậy. Tôi có thể trên thực tế có một mảng const có giá trị khác nhau cho các trường hợp khác nhau của lớp, nhưng giá trị không bao giờ thay đổi trong suốt thời gian tồn tại của lớp
Nathan Fellman

tốt, nếu phương thức khởi tạo của bạn không có tham số, thì tất cả các khởi tạo sẽ có cùng giá trị. Ngoài ra, bạn đúng.

"Tiêu chuẩn ISO C ++ không cho phép bạn" - đó là một ý tưởng tốt để xác định các phiên bản của ISO C ++ tiêu chuẩn mà bạn có trong tâm trí
mloskot


3

Một giải pháp mà không sử dụng heap with std::vectorlà sử dụng boost::array, mặc dù bạn không thể khởi tạo các thành viên mảng trực tiếp trong hàm tạo.

#include <boost/array.hpp>

const boost::array<int, 2> aa={ { 2, 3} };

class A {
    const boost::array<int, 2> b;
    A():b(aa){};
};

3

Làm thế nào về việc mô phỏng một mảng const thông qua một hàm truy cập? Nó không tĩnh (như bạn đã yêu cầu) và nó không yêu cầu stl hoặc bất kỳ thư viện nào khác:

class a {
    int privateB[2];
public:
    a(int b0,b1) { privateB[0]=b0; privateB[1]=b1; }
    int b(const int idx) { return privateB[idx]; }
}

Vì a :: privateB là riêng tư nên nó thực sự là hằng số bên ngoài a :: và bạn có thể truy cập nó tương tự như một mảng, ví dụ:

a aobj(2,3);    // initialize "constant array" b[]
n = aobj.b(1);  // read b[1] (write impossible from here)

Nếu bạn sẵn sàng sử dụng một cặp lớp, bạn cũng có thể bảo vệ privateB khỏi các hàm thành viên. Điều này có thể được thực hiện bằng cách kế thừa một; nhưng tôi nghĩ tôi thích bài đăng comp.lang.c ++ của John Harrison bằng cách sử dụng lớp const.


Đây là một cách tiếp cận thú vị! Cảm ơn!
Nathan Fellman

2

Điều thú vị là trong C #, bạn có từ khóa const dịch thành hằng số tĩnh của C ++, trái ngược với chỉ đọc, chỉ có thể được đặt tại các hàm tạo và khởi tạo, ngay cả bởi các hằng số không, ví dụ:

readonly DateTime a = DateTime.Now;

Tôi đồng ý, nếu bạn có một mảng được xác định trước const, bạn cũng có thể làm cho nó tĩnh. Tại thời điểm đó, bạn có thể sử dụng cú pháp thú vị này:

//in header file
class a{
    static const int SIZE;
    static const char array[][10];
};
//in cpp file:
const int a::SIZE = 5;
const char array[SIZE][10] = {"hello", "cruel","world","goodbye", "!"};

tuy nhiên, tôi đã không tìm ra cách xoay quanh hằng số '10'. Lý do là rõ ràng, mặc dù nó cần nó để biết cách thực hiện truy cập vào mảng. Một giải pháp thay thế khả thi là sử dụng #define, nhưng tôi không thích phương pháp đó và tôi #undef ở cuối tiêu đề, với nhận xét để chỉnh sửa ở đó tại CPP cũng như trong trường hợp có thay đổi.

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.