如何在不知道它有哪些构造函数的情况下初始化一个属性?

huangapple go评论158阅读模式
英文:

How can I initialize an attribute without knowing which constructors he has?

问题

我需要实现以下类:

template <class Element, class Compare = std::equal_to<Element>>
class UniqueArray {
    Element* data;
    unsigned int size;
    unsigned int max_size;
public:
    explicit UniqueArray(unsigned int size);
    UniqueArray(const UniqueArray& other);
    ~UniqueArray();
    UniqueArray& operator=(const UniqueArray&) = delete;
    unsigned int insert(const Element& element);
    bool getIndex(const Element& element, unsigned int& index) const;
    const Element* operator[] (const Element& element) const;
    bool remove(const Element& element);
    unsigned int getCount() const;
    unsigned int getSize() const;
};

问题是我不能假设 Element 具有默认构造函数。假设我不能看到 Element 类的实现,这意味着可能有其他构造函数,但我不知道有多少参数以及它们的类型。我如何初始化 UniqueArraydata 属性?

例如,Element 可以是一个具有两个参数的构造函数(没有默认构造函数)的 Point。但关键是我不知道正在发送哪个元素,也不知道这个元素具有哪个构造函数。代码应该是通用的。

英文:

I need to implement the following class :

template &lt;class Element, class Compare = std::equal_to&lt;Element&gt;&gt;
class UniqueArray {
    Element* data;
    unsigned int size;
    unsigned int max_size;
public:
    explicit UniqueArray(unsigned int size);
    UniqueArray(const UniqueArray&amp; other);
    ~UniqueArray();
    UniqueArray&amp; operator=(const UniqueArray&amp;) = delete;
    unsigned int insert(const Element&amp; element);
    bool getIndex(const Element&amp; element, unsigned int&amp; index) const;
    const Element* operator[] (const Element&amp; element) const;
    bool remove(const Element&amp; element);
    unsigned int getCount() const;
    unsigned int getSize() const;
};

The problem is I can't assume that Element has a defualt constructor.
Assuming I can't see the implementation of Element class meaning that there might be other constructors but I don't know how much parameters there are and what are their types.
How can I initialize the data attribute of UniqueArray?

For example Element can be a Point which has a constuctor with two arguments(and no defualt constructor)
But the point is I don't know which element is being sent and I don't know what constructor this Element has.
The code supposed to be generic.

答案1

得分: 1

不清楚构造函数 UniqueArray(unsigned int size) 应该做什么 - 是默认构造指定数量的元素还是仅保留存储空间?

如果它仅仅是 保留 存储空间,那么您可以使用 std::aligned_storage 来将存储分配与对象构造分离,并让 UniqueArray 的用户创建元素:

template <class Element, class Compare = std::equal_to<Element>>
class UniqueArray {
    using storage_type = typename std::aligned_storage<sizeof(Element), alignof(Element)>::type;
    storage_type* data;
    unsigned int size;
    unsigned int max_size;
public:
    explicit UniqueArray(unsigned int size) : size(0), max_size(size) {
        data = new storage_type[size];
    }
    ~UniqueArray() {
        for (unsigned pos = 0; pos < size; ++pos) {
            // 注意:根据 C++17 需要 std::launder
            reinterpret_cast<Element*>(&data[pos])->~Element();
        }
        delete[] data;
    }
    Element& operator[](unsigned pos) {
        // 注意:根据 C++17 需要 std::launder
        return *reinterpret_cast<Element*>(&data[pos]);
    }
    void insert(const Element& element) {
        new (&(*this)[size++]) Element(element); // placement-new
    }
    // ...
};

struct A {
    int a, b;
    A(int x) : a(x), b(x + 5) {}
};
int main() {
    UniqueArray<A> arr(3);
    arr.insert({ 2 });
    std::cout << arr[0].b << std::endl; // 输出 "7"
}
英文:

It's unclear what the constructor UniqueArray(unsigned int size) is supposed to do - default-construct that many elements or reserve storage?

If it only reserves storage, then you could use std::aligned_storage to decouple storage allocation from object construction, and let the user of UniqueArray create the elements:

template &lt;class Element, class Compare = std::equal_to&lt;Element&gt;&gt;
class UniqueArray {
    using storage_type = typename std::aligned_storage&lt;sizeof(Element), alignof(Element)&gt;::type;
    storage_type* data;
    unsigned int size;
    unsigned int max_size;
public:
    explicit UniqueArray(unsigned int size) : size(0), max_size(size) {
        data = new storage_type[size];
    }
    ~UniqueArray() {
        for (unsigned pos = 0; pos &lt; size; ++pos) {
            // note: needs std::launder as of C++17
            reinterpret_cast&lt;Element*&gt;(&amp;data[pos])-&gt;~Element();
        }
        delete[] data;
    }
    Element&amp; operator[](unsigned pos) {
        // note: needs std::launder as of C++17
        return *reinterpret_cast&lt;Element*&gt;(&amp;data[pos]);
    }
    void insert(const Element&amp; element) {
        new (&amp;(*this)[size++]) Element(element); // placement-new
    }
    // ...
};

struct A {
    int a, b;
    A(int x) : a(x), b(x + 5) {}
};
int main() {
    UniqueArray&lt;A&gt; arr(3);
    arr.insert({ 2 });
    std::cout &lt;&lt; arr[0].b &lt;&lt; std::endl; // prints &quot;7&quot;
}

答案2

得分: 1

首先,收集需求:

  • "std:equal_to 是我可以从标准库中使用的唯一东西"
  • 有一个 max_size 和一个 size 成员,getCount() 返回当前大小,getSize() 返回最大大小(附注:这是可怕的...)

所以我会假设你的类的构造函数应该分配足够的内存来容纳 size(参数)个 Element 类的对象。

正确的方法是 - 如rustyx的答案中建议的 - 使用 std::aligned_storage。由于这是为了大学作业,你的讲师/教授/编写此任务的人可能对C++了解不足,不关心/不理解问题,所以最好使用类似以下方式:

// 在构造函数中,我重申,这是可怕的!
max_size = size; // 更好的做法是使用成员初始化,但是
data = reinterpret_cast<Element*>(new unsigned char[sizeof(Element) * max_size]);

在插入时,使用定位new来创建一个新的元素副本:

new (data[index_for_new_element]) Element(element);

在擦除时,你需要手动调用析构函数!

data[index_to_erase].~Element();

我再次强调:这不是你在真实应用程序中编写此类代码的方式!它不会教给你任何有价值的东西!

英文:

First, gathering requirements:

  • "std:equal_to is the only thing I can use from the standard library"
  • There's a max_size and a size member, getCount() returns the current size and getSize() the max size (side note: this is horrible ...)

So I'd assume that the constructor of your class should allocate enough memory to hold size (the parameter) objects of the Element class.

The proper way to do this would be - as suggested in rustyx' answer - to use std::aligned_storage. Since this is for a university homework and your lecturer / professor / person who wrote this assignment probably doesn't know enough about C++ to care / understand the issues you probably are best of with something like this:

// in constructor, I repeat, THIS IS HORRIBLE!
max_size = size; // better use member initialization, though
data = reinterpret_cast&lt;Element*&gt;(new unsigned char[sizeof(Element) * max_size]);

When inserting you use placement new to create a new element as a copy:

new (data[index_for_new_element]) Element(element);

For erasing you need to call the destructor manually!

data[index_to_erase].~Element();

I repeat: This is not how you'd write such code in a real application! It does not teach you anything valuable!

答案3

得分: 0

以下是翻译好的部分:

"After a discussion with a tutor this is the way to go about it:
data 成员变量将被初始化为一个空指针数组。
每个指针指向一个元素。

我们可以假设存在一个复制构造函数。
每当我们想要将一个新元素插入数组时,我们调用 insert(),这个函数插入一个作为参数传递的元素的副本。"

英文:

After a discussion with a tutor this is the way to go about it :

data member variable will be initialized to an array of null pointers.
each pointer points to an Element.

We can assume that there is a copy constructor.
Whenever we want to insert a new element to the array we call insert() and this function inserts a copy of the element it got as an argument.

huangapple
  • 本文由 发表于 2020年1月6日 19:03:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/59610933.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定