在C ++中的类初始化程序中初始化const数组


76

我在C ++中有以下课程:

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

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

问题是,鉴于b不能在构造函数的函数体内进行初始化,我如何在初始化列表中初始化b const

这不起作用:

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

编辑:恰当的例子是当我可以b为不同的实例使用不同的值时,但已知这些值在实例的生存期内是恒定的。

Answers:


34

就像其他人说的那样,ISO C ++不支持该功能。但是您可以解决它。只需使用std :: vector即可。

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

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

12
问题在于它使用向量会导致额外的开销。
vy32 2010年

1
问题不在于它使用向量,还是一种内存与另一种内存。这是因为您不能直接使用任意一组值来初始化向量。@CharlesB的技术将与boost或std一起工作,分两步进行。
Rick Berge

1
您可以始终使用astd::array来避免某些开销。
bremen_matt

78

使用C ++ 11,此问题的答案现已更改,您实际上可以执行以下操作:

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;
}


12

std::vector使用堆。真是的,这只是为了进行const健全性检查而浪费。重点std::vector是在运行时动态增长,而不是在编译时应进行的任何旧语法检查。如果您不打算扩展,则创建一个类来包装普通数组。

#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;
}

ConstFixedSizeArrayFiller并且ConstFixedSizeArray是可重用的。

第一个允许在初始化数组时进行运行时边界检查(与向量可能相同),此后可以const在初始化之后使用。

第二个允许将数组分配另一个对象内,该对象可以在堆上,或者如果是对象所在的地方,则可以只是堆栈。从堆分配资源没有浪费时间。它还在数组上执行编译时常量检查。

b_filler是提供初始化值的微型私有类。数组的大小是在编译时使用模板参数进行检查的,因此不会超出范围。

我敢肯定,还有更多异国情调的方法可以对此进行修改。这是最初的刺伤。我认为您几乎可以弥补编译器类的缺点。


3
它在堆上有什么关系?内存将在对象的整个生命周期内使用,无论它是在堆上还是在堆栈上。考虑到许多体系结构在同一块内存的相对侧具有堆和堆栈,因此从理论上讲它们可以在中间相遇,为什么对象驻留在哪里很重要?
内森·费尔曼

2
@Nathan Fellman:这可能被视为过度优化,但在某些情况下,您希望对象进行零分配(用于堆栈)。在这种情况下,anew太多了,如果您在编译时知道需要多少,甚至更多。例如,std :: vector的某些实现确实在内部缓冲区中分配了其项,而不是使用new,使得小型向量的构造/销毁相当便宜。
paercebal 2012年

有时,编译器会进行充分的优化,以使std::vector数组产生完全相同的代码。真是的
塞巴斯蒂安·马赫

9

ISO标准C ++不允许您这样做。如果这样做,语法可能是:

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

或类似的规定。从您的问题来看,实际上听起来像您想要的是数组的常量类(又称静态)成员。C ++确实可以做到这一点。像这样:

#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;
}

输出为:

A::a => 0, 1

当然,由于这是一个静态类成员,因此对于类A的每个实例都是相同的。如果这不是您想要的,即您希望A的每个实例在数组a中具有不同的元素值,那么您将试图使数组以const开头的错误。您应该这样做:

#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
为什么使数组const开始是错误的?如果我希望这些值在实例的生命周期中保持不变,例如某种ID,该怎么办?
Nathan Fellman

然后,您应该使用枚举类型。
orj

1
我将如何在此处使用枚举类型?
内森·费尔曼

4

在我有一个常量数组的地方,它总是作为静态完成的。如果您可以接受,则此代码应编译并运行。

#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
假设我确实确实需要一个静态成员,但并非总是如此。实际上,我可以拥有一个const数组,该类对于该类的不同实例具有不同的值,但是在该类的生存期内这些值永远不会改变
Nathan Fellman

好吧,如果您的构造函数不带参数,那么所有实例化都将具有相同的值。除此之外,您是对的。

“ ISO标准C ++不允许您使用”-建议您确定要使用哪个版本的ISO C ++标准
mloskot 2012年


3

不使用堆with的解决方案std::vector是使用boost::array,尽管您不能直接在构造函数中初始化数组成员。

#include <boost/array.hpp>

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

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

3

如何通过访问器函数模拟const数组?它是非静态的(根据您的要求),并且不需要stl或任何其他库:

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

因为a :: privateB是私有的,所以它在a ::之外实际上是常量,您可以像访问数组一样访问它,例如

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

如果您愿意使用一对类,则可以另外保护privateB免受成员函数的侵害。这可以通过继承a来完成。但是我想我更喜欢John Harrison的使用const类的comp.lang.c ++帖子。


这是一种有趣的方法!谢谢!
内森·费尔曼

2

有趣的是,在C#中,关键字const可转换为C ++的静态const,而readonly则只能在构造函数和初始化中设置,即使是非常量也是如此,例如:

readonly DateTime a = DateTime.Now;

我同意,如果您有一个const预定义数组,也可以将其设为静态。此时,您可以使用以下有趣的语法:

//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", "!"};

但是,我没有找到解决常数“ 10”的方法。尽管原因很明显,但它需要知道如何执行对阵列的访问。一个可能的替代方法是使用#define,但是我不喜欢该方法,并且我在标题的末尾#undef,并在CPP处进行了注释,以防万一发生更改。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.