如果只声明了复制构造函数,如何创建第一个对象?

时间:2017-05-15 21:08:00

标签: c++ copy-constructor default-constructor

我今天在网上看到了一个C ++ 03示例。

class Cat {
    public:
        Cat(const Cat& iCat);
};

有人告诉我,在这种情况下,编译器不会自动生成默认构造函数。如果为true,则表示可以从现有Cat对象创建新的Cat对象。

在这种情况下,有人能告诉我如何创建第一个Cat对象吗?如果我的理解是错误的,请善意地纠正我。

5 个答案:

答案 0 :(得分:5)

  

在这种情况下,有人能告诉我如何创建第一个Cat对象吗?如果我的理解是错误的,请善意地纠正我。

这里唯一有效的答案是:

无法使用您发布的代码。您可能错过了一些与您看到的示例一起提供的其他功能。

如果有任何其他的构造器被声明private,有很多方法,例如:

class Cat {
    public:
        Cat(const Cat& iCat);
        static Cat* CreateCat(const std::string& color) {
            return new Cat(color);
        }
    private:
        Cat(const std::string& color)
};

答案 1 :(得分:3)

首先,有效的答案应该是无法创建第一个Cat。这个例子可能只是为了演示用户声明的构造函数如何阻止声明隐式声明的默认构造函数。

现在,尽管如此,并且纯粹如果手段证明了目的是正确的,但是,有一些方法可以使用布局兼容的对象创建第一只猫。

C ++ 11引入了布局兼容性:

  

如果两个标准布局结构(第9类)类型具有相同数量的非静态数据成员,并且相应的非静态数据成员(按声明顺序)具有布局兼容类型(3.9),则它们是布局兼容的。

           

标准布局类是一个类:

     
      
  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
  •   
  • 没有虚函数(10.3),没有虚基类(10.1),
  •   
  • 对所有非静态数据成员具有相同的访问控制(第11条),
  •   
  • 没有非标准布局基类
  •   
  • 在大多数派生类中没有非静态数据成员,并且最多只有一个具有非静态数据成员的基类,或者没有具有非静态数据成员的基类,并且   *没有与第一个非静态数据成员相同类型的基类。
  •   
     

标准布局结构是使用类键struct或类键class定义的标准布局类。

这意味着您可以:

class Cat {
public:
    Cat(const Cat& iCat) : x{iCat.x} {}
    int x;
};

class foo {
public:
    foo(int x) : m_x{x} {}
    int m_x;
};

int main() {
    foo f{5};
    Cat* c1 = reinterpret_cast<Cat*>(&f);
    Cat c2 = *c1;
    std::cout << c2.x << std::endl; // 5
}

xm_x成员用于演示(布局兼容)成员的复制。

注意:正如@M.M的评论中所提到的,您可能需要在编译器中禁用严格别名才能使其正常工作。

答案 2 :(得分:1)

利用C ++ 14 [class.mem] / 18:

  

如果标准布局联合包含两个或多个共享公共初始序列的标准布局结构,   如果标准布局联合对象当前包含这些标准布局结构之一,则允许   检查其中任何一个的共同初始部分。两个标准布局结构共享一个共同的初始值   序列,如果相应的成员具有布局兼容类型,并且成员都不是位字段或   两个都是具有相同宽度的位字段,用于一个或多个初始成员的序列。

'collapse collapsing'是标准布局,因此我们可以创建一个包含两种常见初始序列的联合。任何两个没有数据成员的标准布局类都符合具有公共初始序列的标准,因此:

Cat

注意:标准没有精确定义“检查公共初始部分”的含义。如果公共初始部分与整个结构一致,那是否意味着整个结构可以像我的代码一样进行检查?我想这对语言律师来说是一个问题。

答案 3 :(得分:-2)

你可以这样做,虽然根本不推荐;做下面显示的是非常非常糟糕练习并导致“未定义的行为”

 const Cat* pCat = nullptr;
 const Cat& cat = *pCat; // yikes! NULL reference...boo!
 Cat fluffy(cat);

如果你正在尝试做这样的事情,你可能会以完全错误的方式解决问题 ,你应该重新考虑你的解决方案。

答案 4 :(得分:-2)

请勿在生产代码中执行以下操作,但就插图而言,以下内容适用于“gcc 5.4.0”。

由于涉及UB(未定义的行为),因此在此处使用std :: string,例如作为cat的成员变量,该示例被剥离为:

为了让“猫”成为生命,你可以做一些重新诠释以创造你的“第一只”猫:

class Cat {
    public:
        Cat(const Cat& iCat) {
        }
};

int main() {        
    Cat* cat = reinterpret_cast<Cat*>(new char[sizeof(Cat)]);
    Cat cat2 = Cat(*cat);
    delete cat;
}

同样,上面的代码仅用于说明目的,仅适用于gcc 5.4.0,并不保证它可以与其他编译器一起使用。

有可能在代码中轻松引入UB,通过扩展它,如注释中所述:

#include <iostream>

class Cat {
    private:
        std::string name_;
    public:
        Cat(const Cat& iCat) {
           this->name_ = iCat.name_;
        }
        void setName(const std::string& name) { name_ = name; }
        const std::string& name() { return name_; }
};

int main() {        
    Cat* cat = reinterpret_cast<Cat*>(new char[sizeof(Cat)]);
    cat->setName("Lilly");
    Cat cat2 = Cat(*cat);
    std::cout << cat2.name() << std::endl;
    delete cat;
}