zoukankan      html  css  js  c++  java
  • Effective C++学习之尽量少做转型动作

    正如C++之父所说,在C++中应该尽量少做类型转换,太多的类型转换是一种设计不合格的表现。下面就一起来学习一下Effective C++中对这一条的总结吧,我觉得首先需要C++里面的几种类型转换稍作说明:

      const_cast:和CV限定相关,在实际代码中很少看到使用const_cast的,如果你真的希望在const 函数中改变某个成员变量的话可以将他定义为mutable。恩,还有非cosnt 装换为cosnt 呢?我是没遇到过。

      dynamic_cast:当你希望做该种类型转换时可能希望是在一个derived class上执行derived class的方法,但你只有一个指向base class的指针或引用时可能需要dynamic_cas。我们公司的代码中不采用C++的异常机制,代码中也严禁使用dynamic_cast。

      static_cast:用来表示强制类型转换,比如我们希望将double型变量转换成int类型,void *的指针转换为double *的指针的时候就可以使用static_cast。由于C++中类型有提升这一说,当你将一个小类型的变量赋值给一个大类型变量比如int 变量赋值给double 型变量,这时候编译器会帮你做类型的提升。当你希望将一个double型变量赋值给一个int型变量时编译器就会发出警告,在这种情况下你可以考虑static_cast。不过当你使用staic_cast的时候也承担起了因为从大类型到小类型而引起的精度损失。

      reinterpret_cast:这是一个很低级的类型转换,他会重新解释变量的位模式,比如你希望将一个int 转换为int *,将一个int 的指针转换为char *。转换的越是低级我们就更应该知道进行这种行为的危险性,呃,意思是我在代码中不希望看到有这种的类型转换。可以看下以下的例子:

    int *iptr;
    char *cstr = reinterpret_cast<char*>(iptr);
    string str(cstr);

    呃,我想你是不会写出这样的代码的,但当你将cstr作为一个参数传递给另外一个函数,更倒霉的是这个函数是别人提供的。编译时没有问题,大家都很高兴的下班了。我们的生活就像时针一样转动着--->上班----->下班----->上班---->下班-->....,第二天太阳升起的开始你也开始了第二天的工作。恩,这时你把昨天的写的代码上到机器上想实机测试一吧,你就会发现机器经常出现莫名奇妙的崩溃,这个bug绝对会让你查的死去活来,因为你将一个本该在编译期间出现的问题延后到了运行期。

      其实书上举了几个有意思的例子,第一个就是将一个base class的指针指向derived class的时候会发生位偏移。请看一下demo程序的输出结果:

    class A {
    private:
        int a;
    };
    
    class B {
    private:
        int b;
    };
    class C :public A, public B {
    private:
        int c;
    };
    
    int main()
    {
        C cc;
        A *ptra = &cc;
        B *ptrb = &cc;
        cout << &cc << endl;
        cout << ptra << endl;
        cout << ptrb << endl;
    }

    输出结果:

    0xbf95d28c
    0xbf95d28c
    0xbf95d290

    通过这个程序也很明显的看出单一对象可能有多个地址。发现C++的有些特点让你摸不着头脑,好多细节的东西都会让你想很久,之后才会发出一声"啊哈!!!!! 原来是这样"的感叹。

    啊哈!!!!假如在一个子类中将其强制装换为父类后调用父类的方法,会构造一个临时的父类对象然后在该对象上调用相应的函数。请看下面一个例子,我在子类的set函数中想要先调用了父类中set方法。

    class A
    {
    public: A():t(0) { cout << "construct A" << endl; } virtual void set(int para_a) { cout << "resize A" << endl; t = para_a; } virtual int get() { return t; } private: //A(const A&); //const A& operator=(const A&); int t; }; class B: public A { public: B():A() { cout << "Construct B" << endl; } virtual void set(int para_a) { static_cast<A>(*this).set(para_a); //A::set(para_a); cout << "resize B" << endl; } }; int main() { B b; b.set(4); cout << b.get() << endl; }

    你完全想不到程序的输出为什么是0,之前对这一节盯着看了好几遍都未能理解这里为什么会发生这么怪异的事情,于是一步步的调试首先按照之前的建议将copy构造函数和赋值运算符重载函数声明为私有的,之后就出现了如下的错误:  

    test_2.cpp: In member function ‘virtual void B::set(int)’:
    test_2.cpp:22:2: error: ‘A::A(const A&)’ is private
    test_2.cpp:35:23: error: within this context

    看到这个错误提示才完全明白了为什么set方法正确设置t的之的原因了。发现看程序相关的书的话关键敲代码。首先将书中的例子过一遍,然后认真的推敲一下书中这样写代码的优点在哪里,有那些地方是需要我们借鉴的。相信,这样看书的话一定收获不少。

    最后看看书中给出的避免使用dynamic_cast的方法和为什么要避免使用的原因吧。书中给出了两种方法:

      1. 使用容器存储指向子类对象的指针,然后对容器中的每一个对象调用特定的方法,但这样就会出现需要多个容器的情况。

      2. 通过在base class内提供各种需要特别处理的virtual函数。不过在其中的只做空实装,在特殊的子类才去做真正的实装。这种发放是不是有点太靠技巧了呢?OOO这种问题是不是在设计模式里有更好的方法呢?迫不及待的想看下传说中的设计模式。等待,等我将<Effective C++>看完。

    自己发现写的越来越差了,希望各位指正。最后关于类型转换推荐一片文章,个人感觉不错:http://coolshell.cn/articles/9543.html

  • 相关阅读:
    URAL 1998 The old Padawan 二分
    URAL 1997 Those are not the droids you're looking for 二分图最大匹配
    URAL 1995 Illegal spices 贪心构造
    URAL 1993 This cheeseburger you don't need 模拟题
    URAL 1992 CVS
    URAL 1991 The battle near the swamp 水题
    Codeforces Beta Round #92 (Div. 1 Only) A. Prime Permutation 暴力
    Codeforces Beta Round #7 D. Palindrome Degree hash
    Codeforces Beta Round #7 C. Line Exgcd
    Codeforces Beta Round #7 B. Memory Manager 模拟题
  • 原文地址:https://www.cnblogs.com/lzh2nix/p/3102837.html
Copyright © 2011-2022 走看看