C ++是否会生成一个新对象?

时间:2013-09-07 02:52:07

标签: c++ casting

如上面的标题所示,我的问题是C ++强制转换是否确实创建了目标类的新对象。当然,在问这个问题之前,我使用过谷歌,MSDN,IBM和stackoverflow的搜索工具,但是我找不到合适的答案。

让我们考虑使用虚拟继承解决钻石问题的以下实现:

#include <iostream>
#include <cstdlib>

struct A
{
  int a;

  A(): a(2) {  }
};

struct B: virtual public A
{
  int b;

  B(): b(7) {  }
};

struct C: virtual public A
{
  int c;

  C(): c(1) {  }
};

struct END: virtual public B, virtual public C
{
  int end;

  END(): end(8) {  }
};

int main()
{
  END *end = new END();
  A *a = dynamic_cast<A*>(end);
  B *b = dynamic_cast<B*>(end);
  C *c = dynamic_cast<C*>(end);

  std::cout << "Values of a:\na->a: " << a->a << "\n\n";
  std::cout << "Values of b:\nb->a: " << b->a << "\nb->b: " << b->b << "\n\n";
  std::cout << "Values of c:\nc->a: " << c->a << "\nc->c: " << c->c << "\n\n";

  std::cout << "Handle of end: " << end << "\n";
  std::cout << "Handle of a: " << a << "\n";
  std::cout << "Handle of b: " << b << "\n";
  std::cout << "Handle of c: " << c << "\n\n";
  system("PAUSE");
  return 0;
}

据我所知,B和C的实际结构,通常由A的嵌入式实例和B的变量组成。 C,被破坏,因为B和C的虚拟A被合并到END中的一个嵌入对象以避免歧义。因为(我一直认为)dynamic_cast通常只会增加指针存储的地址乘以嵌入(强制转换)目标类的偏移量,因为目标(B或C)类被分成几个类,所以会出现问题部分。

但是,如果我使用MSVC ++ 2011 Express运行示例,一切都会按预期发生(即它将运行,所有* .a输出2),指针只是略有不同。因此,我怀疑演员表只是通过B / C实例的内部偏移来移动源指针的地址。

但是怎么样?生成的B / C实例如何知道共享A对象的位置。由于END对象中只有一个A对象,但通常是B和C中的A对象,因此B或C必须没有A的实例,但实际上两者似乎都有一个实例。

或者virtual是否仅将对A成员的调用委托给中心A对象而不删除从A继承虚拟的每个基类的相应A对象(即virtual实际上不会破坏内部结构继承及其中的嵌入对象,但只是不使用他们的虚拟化(=共享)成员?

或者virtual是否创建了一个新的“偏移量图”(即地图,它告诉所有成员的地址偏移相对于指向类实例的指针,我不知道实际术语)他们的“分布式”?

我希望我已经澄清了一切,非常感谢提前 BlueBlobb

PS:
我很抱歉,如果有一些语法错误,我只是一个爱巴伐利亚人的啤酒,而不是母语人士:P

修改
如果添加了这些行来输出所有int的地址:

  std::cout << "Handle of end.a: " << &end->a << "\n";
  std::cout << "Handle of a.a: " << &a->a << "\n";
  std::cout << "Handle of a.b: " << &b->a << "\n";
  std::cout << "Handle of a.c: " << &c->a << "\n\n";

它们意味着确实只有一个A物体。

4 个答案:

答案 0 :(得分:6)

  

我的问题是C ++强制转换是否确实创建了目标类的新对象。

是的,对类类型的强制转换会创建该类型的新临时对象。

请注意,您的示例不会在任何地方强制转换为类:它执行的唯一转换是指针类型。这些强制转换确实会创建新的指针实例 - 但不会创建指向的对象。我不确定你的例子应该展示什么,也不确定它与你陈述的问题有什么关系。

此外,在您使用它时,dynamic_cast是不必要的;隐式转换也可以正常工作。

  

因为(我一直认为),dynamic_cast通常只会通过嵌入(强制转换)目标类的偏移来增加指针存储的地址

你必须考虑static_cast或其他什么。 dynamic_cast更强大。例如,它可以从B*转换为C*,即使它们在编译时不相关,也可以转到END*然后备份另一个分支。 dynamic_cast使用运行时类型信息。

  

生成的B / C实例如何知道共享A对象的位置。

这是依赖于实现的。典型的实现将在派生类实例中保留空间以将偏移量存储到其虚拟基类实例。派生程度最高的类的构造函数初始化所有这些偏移量。

答案 1 :(得分:5)

不,你只是看到了多重继承的影响。为了将指针强制转换为不同的基类型,必须将其调整为表示该精确类型的对象部分。编译器知道指针的原始类型和结果类型,因此它可以应用必要的偏移量。为了使派生类型满足“is-a”要求,它必须具有内置的必要结构来模拟所有基类型。

有一种情况是强制转换可以创建一个新对象,当你转换为指针或引用类型以外的类型时。除非您为该类型定义了一个强制转换运算符,否则这通常是不可能的。

答案 2 :(得分:0)

您给出的示例使用指针。

A* a = dynamic_cast<A*>(end);

所以唯一的&#34;新的&#34;这里创建的东西是另一个指针,它将指向&#34; A&#34; &#34;结束&#34;对象的vtable点。它实际上并不构造您正在使用的类/结构类型的新对象。

对比
A a;
B b(a);

这里创建了一个新对象。但是否则,强制转换不会创建目标强制转换类型的新对象。

指针不同的原因是它们指向底层对象的数据部分之前的不同vtable。

示例:

#include <iostream>

using namespace std;

struct A {
    int a[64];
    A() { cout << "A()" << endl; }
    A(const A&) { cout << "A(A&)" << endl; }
    A& operator = (const A&) { cout << "A=A" << endl; return *this; }
};

struct B : virtual public A {
    int b[64];
    B() { cout << "B()" << endl; }
    B(const B&) { cout << "B(B&)" << endl; }
    B(const A&) { cout << "B(A&)" << endl; }
    B& operator = (const B&) { cout << "B=B" << endl; return *this; }
    B& operator = (const A&) { cout << "B=A" << endl; return *this; }
};

struct C : virtual public A {
    int c[64];
    C() { cout << "C()" << endl; }
    C(const C&) { cout << "C(C&)" << endl; }
    C(const B&) { cout << "C(B&)" << endl; }
    C(const A&) { cout << "C(A&)" << endl; }
    C& operator = (const C&) { cout << "C=C" << endl; return *this; }
    C& operator = (const B&) { cout << "C=B" << endl; return *this; }
    C& operator = (const A&) { cout << "C=A" << endl; return *this; }
};

struct END : virtual public B, C {
    int end[64];
    END() { cout << "END()" << endl; }
    END(const END&) { cout << "END(END&)" << endl; }
    END(const C&) { cout << "END(C&)" << endl; }
    END(const B&) { cout << "END(B&)" << endl; }
    END(const A&) { cout << "END(A&)" << endl; }
    END& operator = (const END&) { cout << "END=END" << endl; return *this; }
    END& operator = (const C&) { cout << "END=C" << endl; return *this; }
    END& operator = (const B&) { cout << "END=B" << endl; return *this; }
    END& operator = (const A&) { cout << "END=A" << endl; return *this; }
};

int main() {
    END* end = new END();

    A *a = dynamic_cast<A*>(end);
    B *b = dynamic_cast<B*>(end);
    C *c = dynamic_cast<C*>(end);

    std::cout << "end = " << (void*)end << std::endl;
    std::cout << "a = " << (void*)a << std::endl;
    std::cout << "b = " << (void*)b << std::endl;
    std::cout << "c = " << (void*)c << std::endl;

    // the direct pointers are going to have to differ
    // to point to the correct vtable. what about 'a' in all cases?
    std::cout << "end->a = " << (void*)&(end->a) << std::endl;
    std::cout << "a->a = " << (void*)&(a->a) << std::endl;
    std::cout << "b->a = " << (void*)&(b->a) << std::endl;
    std::cout << "c->a = " << (void*)&(c->a) << std::endl;


}

您可以在此处看到:http://ideone.com/0QAoWE

答案 3 :(得分:0)

至少对于VS 2017中的MSVC,答案也许是肯定的。

/*.thenAccept(renderable -> andyRenderable = renderable)*/
        .thenAcceptBoth(futureTexture, (renderable, texture) -> {
            andyRenderable = renderable;
            andyRenderable.getMaterial().setTexture("baseColor", texture);
        })

// Value is a struct that contains a member: std::string _string; // _value is a std::variant<> containing a Value as one member template <> std::string const &Get<std::string>() const { // Required pre-condition: _value.index() == TYPE_VALUE Value const &value = std::get<TYPE_VALUE>(_value); return static_cast<std::string>(value._string); } std::string const &test() { static std::string x = "hello world"; return static_cast<std::string>(x); } 是一个很大的项目中的一个很小的片段,没有其他几百行代码的支持就无法运行。 Get()是我很快投入研究的东西。

按照书面规定,test()产生以下警告:

  

警告C4172:返回本地变量或临时地址

Get()会编译干净。如果我从test()中删除了static_cast<>,它也会干净地编译。

P.S。事后看来,我应该将Get()重命名为_value之类的东西,因为它包含的内容可能远远超过_payload