C++基础知识总结

1

面试C++程序员的时候一般都是3板斧,先是基础问答,然后一顿虚函数、虚函数表、纯虚函数、抽象类、虚函数和析构函数、虚函数和构造函数。接着拷贝构造函数、操作符重载、下面是STL,最后是智能指针。
       都能挺过去那基本知识这关应该算是过了,下面就是项目背景和算法了。

     1,C++和C相比最大的特点
                   1)面向对象:封装,继承,多态。
                   2)引入引用代替指针。
                   3)const /inline/template替代宏常量。
                   4)namespace解决重名的问题。
                   5)STL提供高效的数据结构和算法
     
     2,你知道虚函数吗
     答案:实现多态所必须,父类类型的指针指向子类的实例,执行的时候会执行之类中定义的函数。
     
     3,析构函数可以是虚函数吗?
     答案: 如果有子类的话,析构函数必须是虚函数。否则析构子类类型的指针时,析构函数有可能不会被调用到。

     4,多态的实现。
     答案:简而言之编译器根据虚函数表找到恰当的虚函数。对于一个父类的对象指针类型变量,如果给他赋父类对象的指针,那么他就调用父类中的函数,如果给他赋子类对象的指针,他就调用子类中的函数。函数执行之前查表。

     5,虚函数表是针对类还是针对对象的?
     答案:虚函数表是针对类的,一个类的所有对象的虚函数表都一样。
     
     6,纯虚函数和虚函数有什么区别
     答案:纯虚函数就是定义了一个虚函数但并没有实现,原型后面加"=0"。包含纯虚函数的类都是抽象类,不能生成实例。

     7,构造函数可以是虚函数吗?
     答案:每个对象的虚函数表指针是在构造函数中初始化的,因为构造函数没执行完,所以虚函数表指针还没初始化好,构造函数的虚函数不起作用。

     8,构造函数中可以调用虚函数吗?
     答案:就算调用虚函数也不起作用,调用虚函数同调用一般的成员函数一样。

     9,析构函数中可以调用虚函数吗?
     答案:析构函数中调用虚函数也不起作用,调用虚函数同调用一般的成员函数一样。析构函数的顺序是先派生类后基类,有可能内容已经被析构没了,所以虚函数不起作用。

     10,虚继承和虚基类?
     答案:虚继承是为了解决多重继承出现菱形继承时出现的问题。例如:类B、C分别继承了类A。类D多重继承类B和C的时候,类A中的数据就会在类D中存在多份。通过声明继承关系的时候加上virtual关键字可以实现虚继承。

2

实现一个自己的String类是一道考验C++基础知识的好题。

至少要能实现以下:构造函数,析构函数,拷贝构造函数(copy constructor),重载赋值操作符(copy assignment operator),。

首先是至少能够准确的写出这几个函数的声明。

扫描二维码关注公众号,回复: 4054547 查看本文章
class String {
public: 
       String();
       String(const char *);
       //旧写法:
       //String(const String& rhs);
       //String& operator=(const String& rhs);
       //新写法:
       String(String rhs);
       String& operator=(String rhs);
       ~String();
private:
       char* data_;
}

其次,老版本的拷贝构造函数和重载赋值操作符时:有几点需要注意的是:判断自己赋值给自己 和 异常安全性。
通过使用swap可以简化方法。
下面是老版本的拷贝构造函数的实现,new的时候有可能会抛出异常。

String::String(const String& rhs) {
    if (&rhs!=this) {
        delete [] data_;
        data_ = new char[rhs.size() + 1];
        memcpy(data_, rhs.c_str(), rhs.size());
    }
    return *this;
}
 
String::~String() {
    delete [] data_;
}


//使用swap的拷贝构造函数,通过swap将临时变量rhs中的数据保存到了data_中,同时data_中的数据拷贝到了临时变量中,在函数返回时会被自动释放。
一举两得,也不用担心有异常发生了。
 

String::String(String rhs) {
      std::swap(data_, rhs.data_);
}
 
String::String& operator=(String rhs) {
      std::swap(data_, rhs.data_);
      return *this;
}
 
String::String() : data_ = new char[1]{
        *data_ = '\0';
}

3

STL相关的各种问题
1,用过那些容器。
最常用的容器就是:vector, list, map, hash_map等等。

2,vector,list,deque的实现。
vector是一块连续内存,当空间不足了会再分配。
list是双向链表。
deque是双端队列可在头和尾部插入、删除元素。

3,hashmap和map有什么区别。
一个是基于hash表实现,一个是基于红黑树实现。

4,红黑树有什么特性

5,STL仿函数和指针的差别。

6,配接器

7,一元、二元仿函数

4

C++面试题(一)、(二)和(三)都搞定的话,恭喜你来到这里,这基本就是c++面试题的最后一波了。
     1,你知道智能指针吗?智能指针的原理。
     2,常用的智能指针。
     3,智能指针的实现。

  1答案:智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放,

     2, 最常用的智能指针: 

              1)std::auto_ptr,有很多问题。 不支持复制(拷贝构造函数)和赋值(operator =),但复制或赋值的时候不会提示出错。因为不能被复制,所以不能被放入容器中。

              2) C++11引入的unique_ptr, 也不支持复制和赋值,但比auto_ptr好,直接赋值会编译出错。实在想赋值的话,需要使用:std::move。

               例如:

                    std::unique_ptr<int> p1(new int(5));
                    std::unique_ptr<int> p2 = p1; // 编译会出错
                    std::unique_ptr<int> p3 = std::move(p1); // 转移所有权, 现在那块内存归p3所有, p1成为无效的指针.

              3) C++11或boost的shared_ptr,基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。

              4)C++11或boost的weak_ptr,弱引用。 引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要手动打破循环引用或使用weak_ptr。顾名思义,weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。

     3, 智能指针的实现

      下面是一个基于引用计数的智能指针的实现,需要实现构造,析构,拷贝构造,=操作符重载,重载*-和>操作符。

template <typename T>
class SmartPointer {
public:
    //构造函数
    SmartPointer(T* p=0): _ptr(p), _reference_count(new size_t){
        if(p)
            *_reference_count = 1; 
        else
            *_reference_count = 0; 
    }
    //拷贝构造函数
    SmartPointer(const SmartPointer& src) {
        if(this!=&src) {
            _ptr = src._ptr;
            _reference_count = src._reference_count;
            (*_reference_count)++;
        }
    }
    //重载赋值操作符
    SmartPointer& operator=(const SmartPointer& src) {
        if(_ptr==src._ptr) {
            return *this;
        }
        releaseCount();
        _ptr = src._ptr;
        _reference_count = src._reference_count;
        (*_reference_count)++;
        return *this;
    }
 
    //重载操作符
    T& operator*() {
        if(ptr) {
            return *_ptr;
        }
        //throw exception
    }
    //重载操作符
    T* operator->() {
        if(ptr) {
            return _ptr;
        }
        //throw exception
    }
    //析构函数
    ~SmartPointer() {
        if (--(*_reference_count) == 0) {
            delete _ptr;
            delete _reference_count;
        }
    }
private:
    T *_ptr;
        size_t *_reference_count;
        void releaseCount() {
        if(_ptr) {
            (*_reference_count)--;
                if((*_reference_count)==0) {
                    delete _ptr;
                    delete _reference_count;
                }
        }
        }
};
 
int main() 
{
    SmartPointer<char> cp1(new char('a'));
    SmartPointer<char> cp2(cp1);
    SmartPointer<char> cp3;
    cp3 = cp2;
    cp3 = cp1;
    cp3 = cp3;
    SmartPointer<char> cp4(new char('b'));
    cp3 = cp4;
}

猜你喜欢

转载自blog.csdn.net/u011808673/article/details/83315842
今日推荐