gpt4 book ai didi

c++ - 转换 “Stylee”

转载 作者:行者123 更新时间:2023-12-01 14:32:45 26 4
gpt4 key购买 nike

从《 Exceptional C++ Solution》一书到ch。 44我了解到,在这些情况下,新的转换样式均无法正常工作。我一直以为它们(这4个新 Actor 表)涵盖了所有可能的情况,并且不再需要“旧”风格的 Actor 表,但这似乎是不正确的。所以我的问题是:
这些新的强制类型转换是否涵盖所有可能的情况,因此不需要使用c样式强制类型转换或:
在某些情况下,只有旧 Actor 才能正常工作?
谢谢。

这是本书的适当片段:

class  A { public: virtual ~A(); /*...*/ }; 
A::~A() { }

class B : private virtual A { /*...*/ };

class C : public A { /*...*/ };

class D : public B, public C { /*...*/ };

A a1; B b1; C c1; D d1;
const A a2;
const A& ra1 = a1;
const A& ra2 = a2;
char c;


void f()
{
A* pa; B* pb; C* pc;
pa = (A*)&ra1;
pa = (A*)&a2;<<----------This is the cast I'm interested in
//This cannot be expressed as a new-style cast. The closest candidate is const_cast,
//but because a2 is a const object, the results of using the pointer are undefined.
//Not my words those are words of Herb Sutter. (whose style of writing irritates me to bits)
pb = (B*)&c1;
pc = (C*)&d1;
}



"

编辑过

来自异常C++的第44章:

“项目44。 Actor

难度:6

您对C++的类型转换了解多少?很好地使用它们可以大大提高代码的可靠性。

标准C++中的新型强制类型转换比旧的强制类型C类型提供了更多功能和安全性。您对他们的了解程度如何?此问题的其余部分使用以下类和全局变量:
class  A { public: virtual ~A(); /*...*/ }; 
A::~A() { }

class B : private virtual A { /*...*/ };

class C : public A { /*...*/ };

class D : public B, public C { /*...*/ };

A a1; B b1; C c1; D d1;
const A a2;
const A& ra1 = a1;
const A& ra2 = a2;
char c;

本项目提出四个问题。

下列哪种新风格的转换不等同于C风格的转换?
  • const_cast
  • dynamic_cast
  • reinterpret_cast
  • static_cast

  • 对于以下每个C样式强制转换,请编写等效的new样式强制转换。如果不写成新的风格,哪一个是不正确的?
    void f() 
    {
    A* pa; B* pb; C* pc;
    pa = (A*)&ra1;
    pa = (A*)&a2;
    pb = (B*)&c1;
    pc = (C*)&d1;
    }

    批判以下每个C++强制转换的样式和正确性。
    void g() 
    {
    unsigned char* puc = static_cast<unsigned char*>(&c);
    signed char* psc = static_cast<signed char*>(&c);

    void* pv = static_cast<void*>(&b1);
    B* pb1 = static_cast<B*>(pv);

    B* pb2 = static_cast<B*>(&b1);

    A* pa1 = const_cast<A*>(&ra1);
    A* pa2 = const_cast<A*>(&ra2);

    B* pb3 = dynamic_cast<B*>(&c1);

    A* pa3 = dynamic_cast<A*>(&b1);

    B* pb4 = static_cast<B*>(&d1);

    D* pd = static_cast<D*>(pb4);

    pa1 = dynamic_cast<A*>(pb2);
    pa1 = dynamic_cast<A*>(pb4);

    C* pc1 = dynamic_cast<C*>(pb4);
    C& rc1 = dynamic_cast<C&>(*pb2);
    }

    为什么通常将const_cast从非const转换为const没用?演示一个有效的示例,其中将const_cast从非const转换为const可能很有用。”

    第44章的解决方案



    让我们一一解答。

    下列哪种新风格的转换不等同于C风格的转换?

    仅dynamic_cast不等效于C样式强制转换。所有其他新式 Actor 表都具有旧式等效项。

    指导方针

    更喜欢新型 Actor 表。

    对于以下每个C样式强制转换,请编写等效的new样式强制转换。如果不写成新的风格,哪一个是不正确的?
    void f() 
    {
    A* pa; B* pb; C* pc;
    pa = (A*)&ra1;

    使用const_cast代替:
      pa = const_cast<A*>(&ra1); 

    pa = (A*)&a2;

    这不能表示为新型 Actor 表。最接近的候选对象是const_cast,但是因为a2是const对象,所以使用指针的结果是不确定的。
      pb = (B*)&c1; 

    使用reinterpret_cast代替:
      pb = reinterpret_cast<B*>(&c1); 

    pc = (C*)&d1;

    上面的强制转换在C语言中是错误的。在C++中,不需要强制转换:
      pc = &d1; 
    }

    批判以下每个C++强制转换的样式和正确性。

    首先,请注意:如果所涉及的类没有虚函数,则以下所有dynamic_casts都将出错。幸运的是,A确实提供了虚函数,使所有dynamic_casts合法。
    void g() 
    {
    unsigned char* puc = static_cast<unsigned char*>(&c);
    signed char* psc = static_cast<signed char*>(&c);

    错误:这两种情况都必须使用reinterpret_cast。起初,这可能会让您感到惊讶,但原因是char,有符号字符和无符号字符是三种不同的类型。即使它们之间存在隐式转换,它们也不相关,因此指向它们的指针也不相关。
      void* pv  = static_cast<void*> (&b1); 
    B* pb1 = static_cast<B*>(pv);

    两者都很好,但是第一个是不必要的,因为已经存在从数据指针到void *的隐式转换。
      B*    pb2 = static_cast<B*> (&b1); 

    这很好,但不必要,因为参数已经是B *。
      A*    pa1 = const_cast<A*>(&ra1); 

    这是合法的,但是抛弃const(或volatile)通常表示风格不佳。您合法地希望删除指针或引用的常量的大多数情况与类成员有关,并且由mutable关键字覆盖。有关常量正确性的进一步讨论,请参见第43条。

    指导方针

    避免抛弃const。使用可变的代替。
      A*    pa2 = const_cast<A*>(&ra2); 

    错误:如果使用指针在对象上写入,这将产生不确定的行为,因为a2实际上是一个const对象。要了解为什么这是一个合法问题,请考虑允许编译器将a2创建为const对象,并使用该信息将其存储在只读存储器中作为优化。在这样的对象上丢弃const显然很危险。

    指导方针

    避免抛弃const。
      B*    pb3 = dynamic_cast<B*>(&c1); 

    潜在的错误(如果尝试使用pb3):由于c1 IS-NOT-AB(因为C不是公开地从B派生的-实际上,它根本不是从B派生的),因此会将pb3设置为null。唯一合法的 Actor 是reinterpret_cast,使用它几乎总是邪恶的。
      A*    pa3 = dynamic_cast<A*>(&b1); 

    可能的错误:因为b1是IS-NOT-AN A(因为B不是公开地来自A;它的派生是私有(private)的),所以除非g()是B的 friend ,否则这是非法的。
      B*    pb4 = static_cast<B*>(&d1); 

    很好,但是不必要,因为可以隐式完成派生到公共(public)基础的指针转换。
      D*    pd = static_cast<D*>(pb4); 

    很好,如果您期望这需要dynamic_cast,可能会让您感到惊讶。原因是当目标已知时,向下转换可以是静态的,但是要当心:您要告诉编译器您知道事实是所指向的实际上就是这种类型。如果您输入的是错误的,则强制类型转换无法将问题通知您(dynamic_cast可能会通知您,如果强制类型转换失败,它将返回空指针),并且充其量,您会得到虚假的运行时错误和/或程序崩溃。

    指导方针

    避免垂头丧气。
      pa1 = dynamic_cast<A*>(pb2); 
    pa1 = dynamic_cast<A*>(pb4);

    这两个看起来非常相似。两者都尝试使用dynamic_cast将B *转换为A *。但是,第一个不是错误,而第二个则不是。

    原因如下:如上所述,您不能使用dynamic_cast将真正是B对象的指针(这里pb2指向对象b1)转换为A对象,因为B是从A私下而不是公开继承的。 ,则第二次强制转换成功,因为pb4指向对象d1,并且D确实将A作为间接公共(public)基类(通过C),并且dynamic_cast能够使用路径B * D * C * A *在整个继承层次结构中进行强制转换。
      C* pc1 = dynamic_cast<C*>(pb4); 

    出于与上一个相同的原因,这也很好:dynamic_cast可以导航继承层次结构并执行交叉转换,因此这是合法的并且将成功。
      C& rc1 = dynamic_cast<C&>(*pb2); 
    }

    最后,一个“异常(exception)”错误:因为* pb2并不是真正的C,所以dynamic_cast将抛出bad_cast异常以指示失败。为什么?好吧,如果指针强制转换失败,dynamic_cast可以并且确实返回null,但是由于没有诸如null引用之类的东西,因此如果引用强制转换失败,它就不能返回null引用。除了抛出异常外,没有其他方法可以向客户端代码发出这样的故障信号,这就是标准bad_cast异常类的用途。

    为什么通常将const_cast从非const转换为const没用?

    前三个问题不包含使用const_cast添加const的示例,例如,将非const指针转换为const指针。毕竟,显式添加const通常是多余的,例如,将非const指针分配给const指针已经合法。通常,我们只需要const_cast做相反的事情。

    问题的最后一部分:演示一个有效的示例,将const_cast从非const转换为const很有用。

    在至少一种情况下,您可以从非const到const有用地const_cast来调用特定的重载函数或特定版本的模板。例如:
    void f( T& ); 
    void f( const T& );
    template<class T> void g( T& t )
    {
    f( t ); // calls f(T&)
    f( const_cast<const T&>(t) ); // calls f(const T&)
    }

    当然,在选择模板的特定版本的情况下,通常通常更容易明确命名它,而不是强制进行正确的演绎。例如,要调用模板函数h()的正确版本,写“h(t)”比写“h(const_cast(t))”要好。

    最佳答案

    在C++中,旧样式的转换是根据新样式的转换定义的。

    5.4:

    4 Any type conversion not mentioned below and not explicitly defined by the user (12.3) is ill-formed.

    5 The conversions performed by — a const_cast (5.2.11), — a static_cast (5.2.9), — a static_cast followed by a const_cast, — a reinterpret_cast (5.2.10), or — a reinterpret_cast followed by a const_cast, can be performed using the cast notation of explicit type conversion.



    第一个项目符号非常清楚地介绍了您提供的示例。在示例末尾您的评论只对了一半。您可以读取结果,但不能写入结果。无论是否使用 const_cast,这都是相同的。基础对象不会因为丢掉它而失去 const -ness。

    稍后的几章中,列出了C样式强制转换与常规 static_cast不同的几种情况。但是它们与沿基类不可访问的继承进行转换有关。您的示例中的 virtual表明书的实际代码中可能有一些继承;也许那是他想要达到的目的,而您却误会了?

    为了完整性:

    7 In addition to those conversions, the following static_cast and reinterpret_cast operations (optionally followed by a const_cast operation) may be performed using the cast notation of explicit type conversion, even if the base class type is not accessible:

    — a pointer to an object of derived class type or an lvalue of derived class type may be explicitly converted to a pointer or reference to an unambiguous base class type, respectively;

    — a pointer to member of derived class type may be explicitly converted to a pointer to member of an unambiguous non-virtual base class type;

    — a pointer to an object of non-virtual base class type, an lvalue of non-virtual base class type, or a pointer to member of non-virtual base class type may be explicitly converted to a pointer, a reference, or a pointer to member of a derived class type, respectively.



    作为最后一个子句所讨论内容的一个示例,这仅是使用C样式强制转换才能实现的。
    class Base { };
    class Derived : Base { };

    Derived d;
    Base* pb;
    pb = static_cast<Base*>(&d); //inaccessible base
    pb = (Base*)(&d); //just fine

    但是,我很难想象这将是一个不错的主意。出于实际目的,仅假设不存在C样式强制类型转换。

    关于c++ - 转换 “Stylee”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3642626/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com