C++ 试题


---------------------------------------------------------------------------

1. C++ 中类与结构体的区别

答:类成员默认访问权限为私有(private),结构体成员默认访问权限为公共(public),其他地方完全一样。

---------------------------------------------------------------------------

2. 关于私有继承

私有继承实际上和组合比较相像,应该说是一个设计概念,而不是具体的实现概念。具体实现中私有继承使用比较少,因为大多数情况使用组合更加清晰。

公有继承意味着 is-a,私有继承意味着 is-implemented-in-terms-of (根据...实现)。


参考:《C++箴言:谨慎使用私有继承》
http://tech.163.com/05/1124/14/23B2S17F0009159Q.html

---------------------------------------------------------------------------

3. 写出下面程序的输出。

#include <stdio.h>

class abc;

void del(abc *pobj)
{
    delete pobj;
}

class abc
{
public:
    abc(){
        printf("abc/r/n");
    }

    ~abc(){
        printf("~abc/r/n");
    }
};


int main(int argc, char *argv[])
{
    abc *pobj = new abc;
    del(pobj);
}

答:
abc

说明:定义 del 函数的时候,abc 的析构函数未定义,因此不会调用。

---------------------------------------------------------------------------

4. 写出下面程序的输出。

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

void * operator new(size_t size)
{
    printf("malloc %u/r/n", size);
    return malloc(size);
}

void operator delete(void * memblock)
{
    printf("free/r/n");
    return free(memblock);
}

class abc
{
public:
    abc()
    {
        printf("abc/r/n");
        throw int();
    }

    ~abc()
    {
        printf("~abc/r/n");
    }
};

int main(int argc, char * argv[])
{
    try
    {
        new abc;
    }
    catch (int & i)
    {
        printf("%d/r/n", i);
    }

    return 0;
}

答:
malloc 1
abc
free
0
如果将“new abc;”换成“abc a;”,结果将是:
abc
0

说明:
1. 在 C++ 中,构造函数抛出异常后不会触发析构函数的调用,这和 object pascal 不一样。C++ 认为构造失败意味着对象没有产生,既然没有生就没有死。然而,当构造函数抛出异常时,仍会调用 delete 函数以释放内存。

operator new 重载全局 new,所以下面构造的时候 new 肯定会调用该 operator new。operator delete 同样。因此先输出 malloc 1。
new 分配完内存后,会自动调用构造函数,所以输出 abc。
在构造函数内部,抛出异常,throw int();
这个异常被捕获,输出 i,其值为 0。
但是在捕获的异常被处理之前,必须先释放内存。因为异常出错,但此时 new 分配内存的工作已经完成,如果不进行 delete 的话,势必会内存泄露。

2. 当生成堆栈对象时,C++ 自动调用的 operator new 和 operator delete 是全局的 operator new 和 operator delete。

参考:
《More Effective C++》条款10: 在构造函数中防止资源泄漏
…… 不用为 BookEntry 中的非指针数据成员操心,在类的构造函数被调用之前数据成员就被自动地初始化。所以如果 BookEntry 构造函数体开始执行,对象的 theName、theAddress 和 thePhones 数据成员已经被完全构造好了。这些数据可以被看做是完全构造的对象,所以它们将被自动释放,不用你介入操作。……

---------------------------------------------------------------------------

5. 写出下面程序的输出。

#include <stdio.h>

template <typename T>
class abc{
public:
    abc(){
        printf("primary/r/n");
    }
};

template<>
abc<int>::abc()
{
    printf("member spec/r/n");
};

template<typename T, typename P>
class abc<T (*)(P)>
{
public:
    abc(){
        printf("partial spec/r/n");
    }
};

int main(int argc, char *argv[])
{
    abc<void* (*)(int)> f_abc;
    abc<int> i_abc;
}

答:
partial spec
member spec

说明:模板部分特化。

---------------------------------------------------------------------------

6. 下面的代码能否通过编译?为什么?

class a
{
public:
    virtual ~a()
    {
    }
private:
    void operator delete(void *p);
};

int main(int argc, char *argv[])
{
    a _1;
}

答:
不能

说明:
1) 如果一个类有虚析构函数的话,那么自定义 delete 函数必须有函数体。
2) 这个题目中,并不会调用 delete 函数。
3) 对于本题,delete 函数不被调用,但是编译器需要它,因此,不能没有定义。

参考:
如果对象是动态创建(也就是 new 出来的),那么在 delete 的时候系统会先调析构函数然后调 operator delete。编译器在编译的时候会把这 2 个步骤合并到一个函数里,看反汇编就知道了,函数名字类似 'scalar deleting destructor'。如果只是声明了 operator delete 函数而没有定义,那么编译的时候会得不到函数地址,这样生成 'scalar deleting destructor' 内置函数的时候就会报错了。

---------------------------------------------------------------------------

7. C 与 C++ 中 static 函数有什么区别?

    C 语言中 static 关键字作用于函数时起限制函数作用域的作用,其 static 函数作用域被限制为当前文件中,该函数定义之后部分。
    C++ 的全局函数用 static 修饰和 C 语言中一个意思 (C++ 标准建议此种情况用匿名名字空间包含该函数来代替 static 关键字);但类成员函数如果用 static 修饰表示是类的作用域而不是对象作用域,可以直接通过类名来引用。

---------------------------------------------------------------------------

8. const 函数的作用。

    类的设计者通过将函数声明为 const 以表明它们不修改类对象。
    const 函数内不允许修改数据成员 (需要注意的是,虽然在 const 函数内,指针成员变量不允许被修改,但对指针所指向内容的修改是允许的)。
    一个 const 类对象只能调用 const 成员函数 (构造和析构函数除外)。

---------------------------------------------------------------------------

9. 拷贝构造函数什么情况下会用到?实现时有什么注意点?

    拷贝构造函数的几个用处:
    1) 用一个类对象初始化该类的另一个对象的时候:
       A a;
       A b(a);
       A b = a;
    2) 传参数和返回时:
       A f (A a) // 传参和返回都会调用拷贝构造函数
       {
           // ...
       }

    实现时要注意的是:
    1) 当类中有指针变量成员时,确认是直接拷贝指针变量的值,还是重新分配内存然后递归拷贝构造。
    2) 是否有每个对象必须有唯一值的成员变量 (比如账号)。其实 1) 也可以归为 2)。

---------------------------------------------------------------------------

10. 写出函数指针,返回指针的函数,const 指针,指向 const 的指针,指向 const 的 const 指针。
    void (* f)()
    void * f()
    int * const f
    const int * f
    const int * const f

---------------------------------------------------------------------------

11. 智能指针。

    智能指针就是模拟指针动作的类。所有的智能指针都会重载 -> 和 * 操作符。
    智能指针还有许多其他功能,比较有用的是自动销毁。这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存。当然,智能指针还不止这些,还包括复制时可以修改源对象等。智能指针根据需求不同,设计也不同(写时复制,赋值即释放对象拥有权限,引用计数等,控制权转移等)。这个主题可以讲一本书。
    auto_ptr 即是一种常见的智能指针。
    智能指针通常用类模板实现:
        template <class T>
        class smpoint
        {
        public:
            smpoint(T * p): _p(p){}
            T & operator * (){return *_p;}
            T * operator -> (){return _p;}
            ~smpoint(){delete _p;}
        private:
            T * _p;
        }

---------------------------------------------------------------------------

12. 标准模板库 vector 追加数据如何实现?注意是底层如何实现,而不是如何使用。

    关键点是,在追加对象前先判断预留的空间是否满足需求,如果不满足则根据分配策略,另分配足够的空间 (一般使用平方增加策略),复制以前的对象数组,再释放原来的空间,然后把对象追加到尾部。如果任何一个操作环节失败,则至少保留原数组不受影响(异常安全保证策略)。

---------------------------------------------------------------------------

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值