文档章节

c++学习

y
 yizhangxyz
发布于 2016/01/31 15:20
字数 3297
阅读 77
收藏 0
  1. const Base b,const Base *pb,一旦声明为const,所有非const成员函数都不能调用了,因为这样可能修改成员变量。public成员变量可以访问,但是不能赋值。

  2. virtual关键字默认继承。构造函数不能定义为虚函数,因为对象构造完成之前,虚表尚未建立,这时候虚函数没意义。析构函数不能定义为纯虚函数,因为在删除派生类的时候会先调用子类的析构函数,再调用父类的析构函数,如果父类析构函数未定义,那么找不到符号会编译出错。

  3. 函数的new ,delete运算符也可以重载。

    void* operator new(size_t size)

    void operator delete(void *memblock),但是注意当类有虚析构函数的时候,delete必须有实现体

  4. 复制构造函数传入的参数必须是引用或者常引用。因为传入副本的话,复制副本的过程会导致无限递归。

  5. struct {int x; int y; int z}Point;

    Point* pPoint = NULL;

    int offset = (int)(&(pPoint)->z);

    结论:offset == 8。&(pPoint)->z表示取point中z的地址。pPoint地址是0,所以加上偏移量8,z的地址就是8.并不需要point的实际地址。改方法可用来求一个变量在对象中的偏移量

     

  6. 结构体也可以有private成员,并且可以继承。它和class的主要区别是它的的成员默认是public的。

  7. 类的函数地址并不存放在对象中,所以在调用成员函数的时候,并不需要对象的地址。但是在访问成员变量的时候需要this指针。

    class A{

    int value;

    public:

    void print1(){cout<<"print1";};

    void print2(){cout<<value;};

    }

    测试:A* a = nullptr; a->print1();a->print2();

    结果:print1正常打印,因为在调用函数的时候把对象的this传递给函数,但在print1并未使用到

    print2崩溃,因为用到this的value变量

  8. 数组和指针的区别

    a、 修改数据的区别:如果指针指向的是常量,那么不能修改数据。
    char* c = "abc";
    c[0] = 'd';//运行时会报错

    b、 sizeof数组得到的是数组的长度,sizeof指针得到的是指针变量的长度,通常是4个字节

  9. overload(重载)和overide(覆盖)overwrite(重写)

    重载表示函数方法名相同,但是参数表不同。c++通过参数表构建函数的全名

    覆盖表示子类重写父类的函数,函数名和参数必须相同,父类函数加上virtual。

    重写表示子类遮掩父类的方法,通常子类函数名和父类相同,但是参数不同。或者参数相同,但是父类没有加上virtual。

  10. delete 和delete[]区别:

    delete[]表示删除数组,并且调用数组每个元素的析构函数,对于基础类型,2个方法没有区别

  11. 引用作为函数返回值的注意事项

    a 临时变量不能作为返回值。因为临时变量会被销毁

    b new 出来的对象也不能作为返回值,因为函数的返回值往往作为一个临时变量来使用,所以导致new分配的内存无法跟踪,导致内存泄漏

    c 类的成员变量可以作为返回值,但最好是const?

  12. 数组的引用与引用的数组

    数组可以有引用,int a [] = {1,1}; a的引用为 int (&b) [2]= a;

    不可以有引用的数组。因为引用是别名,不暂用存储空间。但是数组用连续空间存储变量,所以不能有引用的数组

  13. volatile。表示获取变量的时候都从地址获取。如果没有这个变量则可能优化为从寄存器中获取。可从java学习笔记里面学习。

  14. #ifdef __cplusplus

    extern "C" {

    #endif

    xxxxx

    #ifdef __cplusplus

    }

    #endif

    extern "C":告诉c++编译器,xxxxx是c风格写的代码

  15. 可以通过继承模板单例类来实现单例

    template <typename T>
    class SingleTon{
    public:
        static T *getInstance(){
            static T instance;
            return &instance;
        }
    };

    class A :public SingleTon<A>{
        friend class SingleTon<A>; //class A的构造函数私有。不加上友元的话,父类无法构造子类
    private:
        A(){
        }
    };
    A::getInstance();

  16. RTTI 运行时类型信息,运行时识别对象信息,c++只记录类的名字和继承关系链。

    class Base{};

    class Derived : public Base{
    };

    class Base1{
        virtual void test(){};
    };

    class Derived1 : public Base1{
    };

    Derived d;
    Base& b = d;
    const type_info& typeinfo = typeid(b);
    CCLOG("name=======%s",typeinfo.name());//输出Base
        
     Derived1 d1;
     Base1& b1 = d1;
     const type_info& typeinfo1 = typeid(b1);
     CCLOG("name1=======%s",typeinfo1.name());//输出Derived1

    注意当是指针或者父类赋值没有使用引用的时候下面会输出Base1。RTTI的信息据说存放在虚函数表里。没有虚表的对象不能进行转换。

     

    16.c++ 的类和结构体一样也是需要字节对齐的。类的大小和变量声明顺序有关。

class Test1{
    int a;
    char b;
    int c;
    char d;
};

class Test2{
    int a;
    int c;
    char b;
    char d;
};

经测试,上面2个类的size分别是16和12.

17.public和private继承(is 和has的关系)。public继承认为派生类是基类。而private继承则认为派生类不是基类,他只是使用基类的方法(effective c++)。可用下面的方法测试:

class Base{
};

class Derived1 :public Base{
};

class Derived2 :private Base{
};

Base *b1 = new Derived1(); //ok
Base *b2 = new Derived2();//错误,无法赋值

Derived1 *d1 =  new Derived1();
Derived2 *d2 =  new Derived2();
void test(Base* b){}
test(d1);
test(d2);//这句话编译不通过。

18.以下情况只能用intializationlist 而不能用assignment:
a、const、reference 成员变量 
b、基类的构造函数

19.i++ 和++i的区别:

//i++实现代码为:
int operator++(int)
{
    int temp = *this;
    ++*this;
    return temp;
}

// ++i实现代码为:
int& operator++()
{
    *this += 1;
    return *this;
}//返回自身引用

20. c++ stl中的list是双向链表。因为他可以通过iterator和reverse_iterator来正向和反向遍历。

21.stl中的map采用的是平衡二叉树,通常是红黑树来实现查找。

22.由于list和map都使用链式结构,所以节点在内存中的位置不会发生变化。所以在insert后iterator(指向节点的指针)不会失效。而vector是数组,在insert后iterator就失效了。

22. deque和vector差不多,看起来像是连续空间,但实际上他是由多个连续空间组成的。所以并不存在数据的整体移动。并且他可以在数据的前后两端进行插入和移除。

23.stack是先进后出的,单向开口的。stack只能push和pop,没有遍历的功能,因此没有提供迭代器。deque和list都是双向开口的,因此可通过这2种数据结构来实现stack。

24.queue是先进先出的,后端进,前端出,没有遍历的功能,因此没有提供迭代器。

25.static变量不会占用对象存储空间。const 变量父子对象各有自己的存储。

26.mutable关键字解决const函数不能修改non-static成员变量的限制。

27.inline:inline只是对编译器的申请,而非强制行为。

  • 将函数声明为inline,不一定真的inline,如果函数体过大,编译器是拒绝的。

  • inline函数内部有对virtual函数调用也不行,因为inline函数的替换是在编译时期的,但是编译期无法知道函数对应实体

  • 未声明为inline的函数编译器也可能将他当作inline处理。如下:

    class A{
    int a;
    public:
    int get(){return a;} //这里是inline的隐式声明
    };

28.模板(编译器多态)。

函数模板

template <class T>
void swapValue(T& a, T& b)
{
    T c = b;
    b = a;
    a = c;
}

类模板

template<class T>
class Base
{
public:
    T1 a;
    void test1(){}
    void test2(){}
};

模板特化

template<>
class Base<ClassA>
{
public:
    void test1(){}
};

继承模板

template<class T>
class Derived1 :public Base<T>
{
public:
    void test1()
    {
        test();//这里会报错,因为模板可能进行特化,所以这里并不清楚base类是否有这个方法
    }
};
三种办法解决上面的问题。
(1)this->test();//特指base class templates内的成员名称
(2)using Base<ClassA>::test;
(3)Base<ClassA>::test();

29.初始化列表。成员变量初始化顺序和初始化列表里面的顺序无关,和class里面的声明顺序有关。

class Base
{
private:
    int m_i,m_j;
public:
    Base(int i):m_j(i),m_i(m_j){}
};

比如这样会导致m_i初始化错误。
初始化列表在构造函数之前执行。

30.虚继承。class 派生类名:virtual 继承方式  基类名

为了解决从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题,将共同基类设置为虚基类。这时从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。这样不仅就解决了二义性问题,也节省了内存,避免了数据不一致的问题。

class Base  
{
 public:  
 Base(){} 
 void print(){cout << "Base print" <<endl;}  
};

class Mid1 : virtual public Base  
{
public:  
Mid1(){}  
};

class Mid2 : virtual public Base  
{
 public:  
 Mid2(){}  
};

 class Child:public Mid1, public Mid2  
{
 public:  
 Child(){}  
};

 

如果Mid1,Mid2不加virtual,只能像下面那样使用

d.Mid1::print();
d.Mid2::print();

加了,就可以这样使用d.print();

 

31.多继承类型转换

class Top
{ public: int a;
};
class Left : virtual public Top
{ public: int b;
};
class Right : virtual public Top
{ public: int c;
};
class Bottom : public Left, public Right
{ public: int d;
};

Bottom *b = new Bottom();
    
Left *l = b;
Right *r = b;

注意l和r的值不同

32.stl map和set的区别。

二者都采用红黑树来存放数据

map(映射):存放的是key和value,key不能重复,但是value可以重复

set(集合)  :存放key(key就是value),key不能重复

33.

class A{
    
public:
    A(){
      std::cout << "default construc A"<< std::endl;
    }
    A(int i){
        std::cout << "construc A 1"<< std::endl;
    }
};

A a=1;和A a(1);都是执行的A(int i)构造函数
记住调用无参数构造函数的时候不能 A a(),只能A a;A a();表示名称为a的函数声明。

34.当基类的虚函数为private的时候,也会存放在虚表中,如果子类重写了基类的虚函数。同样具有多态效果。比如:

class Base
{
public:
    void Open()
    {
        OnOpen();
    }
private:
    virtual void OnOpen() = 0;
};

class Derived : public Base
{
public:
   
private:
    void OnOpen()
    {
        cout << "Derived OnOpen----------" << endl;
    }
};

Base *pBase = new Derived();
pBase->Open();//输出Derived OnOpen----------

35 NULL和nullptr区别在于,前者只是一个宏定义,后者是一个真正的类型。应该优先使用nullptr。

36.c++ 构造函数不能互相调用。但是java是可以的。通过this(X,X,X);

37.函数重载
void test(){}   
void test(int a=0){}
调用test的时候会编译报错。调用是模棱两可的。构造函数也是一样的。

38.初始化列表和构造函数类赋值区别在于,构造函数内赋值之前肯定已经经过初始化列表。初始化列表内调用的是构造函数,而构造函数内调用的是拷贝运算符。

39.private变量
class MyString
{
private:
    char *m_data;
public:
    void test(const MyString &another){
        m_data = new char[strlen(another.m_data)+1];
    }
};

在类的方法内可以访问本类其他对象的private成员。java也是一样的。

40.priority_queue:优先队列

priority_queue<Type, Container, Functional>
其中Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。
Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list.
STL里面默认用的是 vector. 比较方式默认用less (operator<), 所以如果你把后面俩个参数缺省的话,
优先队列就是大顶堆,队头元素最大。

priority_queue<int, vector<int>, greater<int> >pq;//greater 和less已经实现了

41.数组名和数组名取地址

数组名和数组名取地址在数值上是相同的,均表示数组第一个元素的地址。但是二者的颗粒度不同。
        当数组是一维数组时,数组名是以一个数组元素为颗粒度,表现为“当数组名加1时,这里的1表示一个数组元素单元”,例子中的数组元素为整数,所以数组名加1时地址加4;而数组名取地址&以整个数组为颗粒度,表现为“当数组名取地址&加1时,这里的1是表示整个数组单元”,例子中的数组为有5个元素的整型数组,所以数组名取地址&加1时,地址加20.
       当数组是二维数组时,数组名array、array[0]、&array[0][0]以及数组名取地址&在数值上是相同的,同样各个之间的颗粒度不同。其中array[0]以及 &array[0][0] 的颗粒度相同,均是以一个数组元素为颗粒度,所以它们加1后,地址加4;而数组名和数组名取地址&颗粒度不同,前者以一行元素为颗粒度,后者以整个数组单元为颗粒度,所以前者加1,地址加3*4,后者加1,地址加6*4.

总结一下:数组名取地址加1移动的是整个数组的长度。
                  二维数组数组名加1移动一行

42.Concurrency::concurrent_queue:高效线程安全队列。

© 著作权归作者所有

共有 人打赏支持
y
粉丝 1
博文 67
码字总数 42366
作品 0
成都
C++基础教程之C/C++区别

C/C++基础教程之C/C++区别 这是C++教程得第一步,后续会持续更新哦!欢迎新手(具有C基础),老鸟可绕道,可指导。 C++标准输入和输出 C++标准输入和输出分别是cin和cout,用法非常简单 cin>>n...

这个人很懒什么都没留下
09/01
0
0
C语言/C++程序员编程学习自信心曲线图

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
05/10
0
0
C语言/C++永远都不会过时的编程语言

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
03/30
0
0
Java程序员如何高效而优雅地入门C++

Java程序员如何高效而优雅地入门Cpp,由于工作需要,需要用C++写一些模块。关于C++ 的知识结构,虽说我有过快速学习很多新语言的经验,但对于C++ 我也算是老手,但也还需要心生敬畏,本文会从...

小欣妹妹
04/23
0
0
C语言/C++编程学习强势之处的体现

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
05/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

xilinx资源

本系列教学视频由赛灵思高级战略应用工程师带领你:从零开始,一步步深入 掌握 HLS 以及 UltraFAST 设计方法,帮助您成为系统设计和算法加速的大拿! http://www.eetrend.com/topics/2018-0...

whoisliang
10分钟前
0
0
=====BJmeter性能测试小接=====

一、性能测试分类 1、负载测试: 通过逐步加压的方法,达到既定的性能阈值的目标,阈值的设定应是小于某个值,如cpu使用率小于等于80% 2、压力测试: 通过逐步加压的方法,使得系统的某些资源...

覃光林
14分钟前
0
0
企业级开源四层负载均衡解决方案--LVS

网盘链接 企业级开源四层负载均衡解决方案--LVS 本课程将在Linux环境下,学习配置使用LVS,对Web集群和MySQL集群进行负载均衡,并结合利用Keepalived实现负载均衡器的高可用,实现对后端Rea...

qq__2304636824
19分钟前
0
0
Windows上安装Spacemacs

emacs安装 下载地址emacs 安装比较简单,解压后执行\bin\addpm.exe即可 emacs配置 emacs的默认配置文件路径和.emacs.d文件夹都是在Windows主目录下的 C:\Users\Administrator\AppData\Roami...

yxmsw2007
35分钟前
0
0
OSChina 周一乱弹 —— 鱼生不值得

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @瘟神灬念:分享新裤子的单曲《没有理想的人不伤心 (Remix版)》: 《没有理想的人不伤心 (Remix版)》- 新裤子 手机党少年们想听歌,请使劲儿戳...

小小编辑
今天
180
9

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部