C和C++中的一些问题

来源:互联网 发布:计算机java语言培训 编辑:程序博客网 时间:2024/06/08 00:11

C和C++中的一些问题

1. C语言是否可以进行重载?

测试代码如下:
    #include <stdio.h>
    #include <stdlib.h>

    int f(int a, char b)
    {
        return a;
    }

    float f(int a, float b)
    {
        return b;
    }

    char f(char a)
    {
        return a;
    }

    int main()
    {
        int a;
        float b;
        char c;
        f(a, c);
        f(a,b);
        f(c);

        return 0;
    }

    从测试代码可以看出,其中包含了重载的基本情况:参数的个数不同和参数的类型不同。

    使用不同的编译器进行测试:
    1. CodeBlocks 8.02中,建立C的工程。
    
    ||=== test, Debug ===|
    \test\main.c|10|error: conflicting types for 'f'|
    \test\main.c|5|error: previous definition of 'f' was here|
    \test\main.c|15|error: conflicting types for 'f'|
    \test\main.c|10|error: previous definition of 'f' was here|
    \test\main.c|15|error: conflicting types for 'f'|
    \test\main.c|10|error: previous definition of 'f' was here|    
    \test\main.c||In function `main':|
    \test\main.c|24|error: too many arguments to function `f'|
    \test\main.c|25|error: too many arguments to function `f'|
    ||=== Build finished: 8 errors, 0 warnings ===|

    编译时使用 mingw32-gcc进行C代码编译。

    2. CodeBlocks 8.02中,建立C++的工程。

    编译成功。其使用mingw32-g++进行C++代码编译。

    3. DEV-C++ 4.9.9,建立C工程:
    
    编译错误,

    10 \test2\main.c conflicting types for 'f'
    5  \test2\main.c previous definition of 'f' was here
    15 \test2\main.c conflicting types for 'f'
    …………

    4. DEV-C++ 4.9.9,建立C++工程:

    编译正确。

    5. VS2003,不区分C工程与C++工程,直接编译成功。

    猜测应该是VS2003将所有代码默认完全按照C++方式进行编译。

    验证猜测,给函数加上extern ” C”
    extern "C"
    {
        int f(int a, char b);
        float f(int a, float b);
        char f(char a);    
    }

    编译结果出现错误:
    \test5\main.cpp(7): error C2733: second C linkage of overloaded function 'f' not allowed
    \test5\main.cpp(8): error C2733: second C linkage of overloaded function 'f' not allowed
    \test5\main.cpp(8) : see declaration of 'f'

    由此我们可以得出结论:C语言无法进行函数重载,C++才可以进行函数重载。具体原因以个人的知识水平只能解释到C++语言所具有的name mangling机制带来的好处。

2. C++中对于成员函数如何编译处理,编译为全局函数?它是如何根据类对函数进行调用的?

    在编译器眼中,同一个函数只存在一个实现,不管是全局函数还是成员函数。那么c++是如何处理类的成员函数的呢?实际上,c++通过name-mangling技术把每一个成员函数都转换成了名字唯一的全局函数,并把通过对象、指针或者引用对每一个成员函数的调用语句都改写成为相应的全局函数调用语句。每一个非静态数据成员函数都会被添加一个本类对象的指针作为第一个参数,这就是this指针的由来,然后再运用name-mangling技术处理。例如Rectangle类的SetLength成员函数被编译器改写后的样子可能是:
    void _SetLength@Rectangle$2F&pf@GS(Rectangle *this, float length)   //全局函数
    {
        this->m_length = length;
    }

    由此调用语句rec1.SetLength(100.5)被改写:
    ::_SetLength@Rectangle$2F&pf@GS(&rec1, 100.5);

    编译器对数据成员也会进行name-mangling处理。不同的c++编译器对class的成员函数、数据成员和全局函数的name-mangling方案是不同的,这是造成不同编译器之间连接兼容性的主要原因之一。

3. C++基类指针指向子类对象数组,析构是存在问题,调用时是否也存在问题?

    主要是由于内存造成的。

    CodeBlocks 中测试如下代码:
    #include <iostream>
    using namespace std;

    class Base
    {
    protected:
        int i;
    public:
        Base();
        virtual ~Base();
    };

    Base::Base()
    {
        cout << "Base constructor" << endl;
    }

    Base::~Base()
    {
        cout << "Base destructor" << endl;
    }

    class Derived : public Base
    {
    protected:
        int j;
    public:
        Derived();
        virtual ~Derived();
    };

    Derived::Derived()
    {
        cout << "Derived constructor" << endl;
    }

    Derived::~Derived()
    {
        cout << "Derived destructor" << endl;
    }

    int main()
    {
        cout<<"Create Single Object:" << endl;
        Base * p = new Derived();
        delete p;
        cout << endl;
    
        cout<<"Create Object array:" << endl;
        Base * base = new Derived[2];
        cout << endl;
    
        cout<<"Delete Object array:" << endl;
        delete [] base;
    
        system("pause");
        return 0;
    }

    结果如下:
    Create Single Object:
    Base constructor
    Derived constructor
    Derived destructor
    Base destructor

    Create Object array:
    Base constructor
    Derived constructor
    Base constructor
    Derived constructor

    Delete Object array:

    Process returned -1073741819 (0xC0000005)   execution time : 2.296 s

    可见在进行析构时,如果是单对象(非数组形式),则不会出现错误。如果是基类指针指向了子类对象数组,即便是用了虚函数,由于Base与Derived的对象大小不同,直接造成delete错误。

    如果将Derived类中的 int j;成员变量注释掉,此时则运行正确。

    结果如下:
    Create Single Object:
    Base constructor
    Derived constructor
    Derived destructor
    Base destructor

    Create Object array:
    Base constructor
    Derived constructor
    Base constructor
    Derived constructor

    Delete Object array:
    Derived destructor
    Base destructor
    Derived destructor
    Base destructor

    Process returned 0 (0x0)   execution time : 0.109 s

    具体原因据个人推断则是由于在调用delete [] base;时,需要对base进行操作,依次析构数组中的每一个元素,进行第二个元素析构时产生,其指针计算方法为 base + sizeof(Base),如果Base与Derived对象大小相同,则不会出现问题,否则第二个对象的指针指向错误,导致析构错误。

    VS2003中不论是否对子类的 int j;进行注释,结果都如下所示:

    Create Single Object:
    Base constructor
    Derived constructor
    Derived destructor
    Base destructor

    Create Object array:
    Base constructor
    Derived constructor
    Base constructor
    Derived constructor

    Delete Object array:
    Derived destructor
    Base destructor
    Derived destructor
    Base destructor

    请按任意键继续. . .

    个人认为是VS中进行了特殊的处理造成的。


4. 关于结构体:

      在纯C的环境下,如下的代码是编译不过的。

        struct base

        {

                int a;

        };


        void main()

       {

                base b;                 // 错误

                struct base b;      // 正确         

       }

      

        或

        typedef struct base

        {

                int a;

        }base;

     

        这样才可以直接使用  base b;

        具体原因不太明白。


        C++中,结构体和类除了默认访问权限不同,其他的都是相同的,因此C++中应该是将结构体直接按照类的编译方式进行编译。因此可以按照上述错误的情况进行变量的声明,而C中则需要使用typedef进行类型别名的声明。