第14课 - 专题二经典问题解析

第14课 - 专题二经典问题解析

    一.malloc与free和new与delete有什么区别?

        (函数)            (关键字)

        1.1 new在申请的时候可以直接初始化

      
 Source Example 1.1:
        #include <iostream>
        #include <stdio.h>
        #include <stdlib.h>

        using namespace std;

        int main()
        {
            int* p = reinterpret_cast<int *>(malloc(sizeof(int)));
            /* new关键字可以在申请的时候初始化 */
            int *q = new int(10);
            
            *p = 5;
            //*q = 10;
            
            /* 输出5 10 */
            cout<<*p<<endl<<*q<<endl;
            
            return 0;
        }

        1.2 new和delete在申请类空间时负责构造函数和析构函数的调用

      
 Source Example 1.2
        #include <iostream>
        #include <stdio.h>
        #include <stdlib.h>

        using namespace std;

        class Test{
            private:
                int i;
            public:
                Test()
                {
                    cout<<"Test()"<<endl;
                    i = 0;
                }
                
                int GetI()
                {
                    return i;
                }
                
                ~Test()
                {
                    cout<<"~Test()"<<endl;
                }
        };

        int main()
        {
            Test* p = reinterpret_cast<Test *>(malloc(sizeof(Test)));
            Test *q = new Test;
        
            /* 输出一个随机数,没有调用构造函数,并不是一个对象 */
            cout<<p->GetI()<<endl;
            /* 输出0,表明调用了构造函数 */
            cout<<q->GetI()<<endl;
            
            /* 如果用delete函数也会调用析构函数 */
            free(p);
            /* 会调用析构函数,也可以用free函数来释放,但是会没有析构函数的调用 */
            delete q;
            
            return 0;
        }   

        输出结果如下:


        

        1.3总结:

            1.3.1 malloc和free是库函数,以字节为单位申请堆内存

            1.3.2 new和delete是关键字,以类型为单位申请堆内存

            1.3.3 malloc和free单纯的对内存进行申请与释放

            1.3.4 对于基本类型new关键字会对内存进行初始化

            1.3.5 对于类类型new和delete还负责构造函数和析构函数的调用

        

        

    二.编译器对构造函数的调用

    2.1 C++编译器会尝试各种手段尝试让程序通过编译

        2.1.1 方式一: 尽力匹配重载函数

        2.1.2 方式二: 尽力使用函数的默认参数

        2.1.3 方式三:尽力尝试调用构造函数进行类型转换

 
   Source Example2.1:
        #include <iostream>
        #include <stdio.h>
        #include <stdlib.h>

        using namespace std;

        class Test{
            private:
                int i;
            public:
                Test(int i)
                {
                    cout<<"Test(int)"<<i<<" "<<endl;
                }
                
                Test(const Test& t)
                {
                    cout<<"Test(const Test& t)"<<endl;
                }
                
                ~Test()
                {
                    cout<<"~Test()"<<endl;
                }
        };
        void func()
        {
            /*
             * "古代"编译器的处理
             *  对于Test t2 = 5; 分析
             * 1. 默认情况下,字面量5的类型为int,因此5无法直接初始化Test对象
             * 2. 但是编译器在默认情况下可以自动调用构造函数,于是编译器就会将5作为参数调用构造函数
             * 3. 调用Test(int)生成一个临时对象
             * 4. 将临时对象用来初始化定义的对象会调用拷贝构造函数
             *
             *  对于Test t3 = Test(5);分析
             * 1. 直接调用Test(int)生成一个临时对象
             * 2. 将临时对象用来初始化定义的对象会调用拷贝构造函数
             *
             * 每次都要创建一个临时对象,效率低下
             *
             *    "现代"编译器会对后两种初始化方式进行优化
             *  Test t1 = 5 <==> Test t1(5);
             *  不会再去创建临时对象,因此这三种初始化方式是等价的
             */
            Test t1(5);
            Test t2 = 5;
            Test t3 = Test(5);
        }


        int main()
        {
            func();    
            
            return 0;
        }

        输出结果如下:


        由于是现代编译器进行编译,并不会创建临时对象,不会去调用拷贝构造函数。

        

    三. "剥夺"编译器对构造函数的调用尝试

        3.1  C++提供了explicit关键字用于阻止编译器对构造函数的调用尝试

        
Source Example 3.1:
            #include <iostream>
            #include <stdio.h>
            #include <stdlib.h>

            using namespace std;

            class Test{
                private:
                    int i;
                public:
                    /* 剥夺了编译器调用构造函数的权利 */
                    explicit Test(int i)
                    {
                        cout<<"Test(int)"<<i<<" "<<endl;
                    }
                    
                    explicit Test(const Test& t)
                    {
                        cout<<"Test(const Test& t)"<<endl;
                    }
                    
                    ~Test()
                    {
                        cout<<"~Test()"<<endl;
                    }
            };
            void func()
            {
                /* 初始化的标准写法,确实想要用构造函数构造这个对象(我们主动调用的),可以调用 */
                Test t1(5);
                /* 将5赋值给t2,编译器发现不对会去尝试调用构造函数,但是无法调用,因此会报错 */
                Test t2 = 5;
                /*
                 * 我们主动调用的,不会报错
                  * 但是需要调用拷贝构造函数进行赋值,但是无法调用,仍然会出错
                 */
                Test t3 = Test(5);
            }


            int main()
            {
                func();    
                
                return 0;
            }
            

    四.类的静态成员能干啥呢?

        4.1 对象数目控制,一个类最多只有一个对象存在系统中,如何实现?

                        一个汽车类只有一个引擎对象

        
Source Example 4.1:(单例模式的实现)
        #include <iostream>
        #include <stdio.h>
        #include <stdlib.h>

        using namespace std;

        class SingnaleTon{
        private:
            static SingnaleTon* pInstance;
            SingnaleTon()
            {
            }
        public:
            static SingnaleTon* GetSignaleTon()
            {
                if (pInstance == NULL)
                {
                    /* 只被打印了一次 */
                    cout<<"I am GetSignaleTon"<<endl;
                    pInstance = new SingnaleTon;    
                }
                
                return pInstance;
            }
            ~SingnaleTon()
            {
                pInstance = NULL;
            }
        };

        SingnaleTon* SingnaleTon :: pInstance = NULL;

        void func()
        {
            SingnaleTon* s1 = SingnaleTon :: GetSignaleTon();  
            SingnaleTon* s2 = SingnaleTon :: GetSignaleTon();  
            SingnaleTon* s3 = SingnaleTon :: GetSignaleTon();
            
            /* 编译这句会报错,因为new的时候要调用构造函数,但是构造函数时私有数据 */
            //SingnaleTon* s3 = new SingnaleTon;
            
            /* 打印的s1,s2,s3的地址相同 */
            cout<<s1<<"  "<<s2<<" "<<s3<<" "<<endl;
        }

        int main()
        {
            func();
                
            return 0;
        }                           

    五.无状态和状态函数

        5.1 无状态函数

            函数的调用结果只与实参值相关

        
Source Example 5.1:
            /* 每一项都会做重复循环,时间复杂度为O(n) */
            int fib1(int i)
            {
                int a1 = 0;
                int a2 = 1;
                int ret = a2;
                
                while (i >= 1)
                {
                    ret = a2 + a1;
                    a1 = a2;
                    a2 = ret;
                    i--;
                }
                
                return ret;
            }

        5.2 状态函数

            函数的调用结果不仅与实参值相关还与之前的函数调用相关

      
 Source Example5.2:
            /* 第n次调用返回斐波那契数列的第n项 */
            /* 时间复杂度为O(1),但是无法从头在来 */
            int fib2()
            {
                static int a1 = 0;
                static int a2 = 1;
                
                int ret = a1 + a2;
                
                a1 = a2;
                a2 = ret;
                
                return ret;
            }      

        5.3 函数对象的实现

         
   #include <iostream>
            #include <stdio.h>
            #include <stdlib.h>

            int fib1(int i)
            {
                int a1 = 0;
                int a2 = 1;
                int ret = 1;
                
                while (i >= 1)
                {
                    ret = a1 + a2;
                    a1 = a2;
                    a2 = ret;
                    i--;
                }    
                
                return ret;
            }

            int fib2()
            {
                static int a1 = 0;
                static int a2 = 1;
                int ret = a1 + a2;
                
                a1 = a2;
                a2 = ret;
                
                return ret;
            }

            class Fib {
                private:
                    int a1;
                    int a2;
                public:
                    Fib()
                    {
                        a1 = 0;
                        a2 = 1;
                    }
                    
                    /* 函数操作符重载! */
                    int operator() ()
                    {
                        
                        int ret = a1 + a2;
                    
                        a1 = a2;
                        a2 = ret;
                        
                        return ret;
                    }
            };

            
            int main()
            {
                Fib fib;
                
                /* 效率不够 */
                for (int i = 1; i < 10; i++)
                {
                    printf ("%d\n", fib1(i));
                }
                
                /* 无法从头再来 */
                for (int i = 1; i < 10; i++)
                {
                    printf ("%d\n", fib2());
                }
                
                /* 再定义一个对象即可从头在来 */
                for (int i = 1; i < 10; i++)
                {
                    printf ("%d\n", fib());
                }
                
                return 0;
            }
          
       
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

第14课 - 专题二经典问题解析

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭