C++基础知识总结
不管是自我定位太高,还是职位层次太低,系统复习了一遍很久没有摸过的C++总是有好处的。总结如下: 一、new和malloc的区别 1、new和delete配对,释放数组需要用delete[]。new和delete实际上调用了malloc和free,另外调用了类的构造函数和析构函数。 3、new申请的内存保存在堆中,malloc申请的内存保存在自由存储区。 二、C++运算符 1、取模操作符:% 三、&: 取地址运算符、定义变量引用 &操作符用于取地址时的用法是:int* x=&y;。 然而,另外一种用法是定义变量别名,这种用法不能和取地址简单等同。用于传递函数输入参数时很好理解,但定义变量时容易引起理解错误,特别是和指针的区别: 从内存的角度看,指针和引用是完全不同的。指针,内存要为它分配一个存储空间。引用,内存不分配空间的,引用只是一个别名。我认为就是在符号表里增加一个标志而已,对于语句int &y=x; (&x=&y)为true。 实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?答案是“用适当的工具做恰如其分的工作”。当重载某个操作符时,你应该使用引用。最普通的例子是操作符[]。这个操作符典型的用法是返回一个目标对象,其能被赋值。如果操作符[]返回一个指针,那么后一个语句就得这样写: *v[5] = 10; 但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用。 引用的一些规则如下: (1)引用被创建的同时必须被初始化(指针则可以在任何时候被初始化),否则会报编译错误。 int i = 5; int j = 6; int &k = i; k = j; // k 和i 的值都变成了6; (3)不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)。 char *pc = 0; // 设置指针为空值 char& rc = *pc; // 让引用指向空值 (4)“sizeof 引用”得到的是所指向的变量(对象)的大小,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)。 (5)引用只能指向一个实际的变量,不能指向指针或引用 class A{...}; class B:public A{...}; void f(A&a1,A&a2) { a1=a2;//此处调用的只有基类A的复制函数,而B部分不会被进行复制,之将导致数据的不一致(即B部分的数据没有被复制); a1.fun(); } 四、关于const 一般的const变量: 下面两个声明都指向一个const int类型的指针,指针所指向的内存不能被修改,但指针可以指向另一个内存: 声明一个指向const int类型的const指针: const在函数声明中的含义: const int& SetPoint(const int& param) const 类的const和static成员变量的初始化: 对于static成员变量,如果同时是const的,可以在类定义中初始化,否则只能在类定义外部初始化。 五、一些数据类型和变量赋值语法 1、union 中的所有被声明的元素占据同一段内存空间,其大小取声明中最长的元素的大小。union 的用途之一是将一种较长的基本类型与由其它比较小的数据类型组成的结构(structure)或数组(array)联合使用。 4、定义一个指向int[4]数组的指针变量 六、常用的几个标准C++函数 1、cout和cin的用法: 2、常用的字符串函数: 七、switch-case的写法 switch (expression) { case constant1: block of instructions 1 break; case constant2: block of instructions 2 break; . . . default: default block of instructions } 八、函数的几个属性和用法 1、指定函数的默认参数值 2、什么是函数重载(Overloaded functions) 3、内联函数 inline 指令可以被放在函数声明之前,要求该函数必须在被调用的地方以代码形式被编译。这相当于一个宏定义(macro)。它的好处只对短小的函数有效,这种情况下因为避免了调用函数的一些常规操作的时间(overhead),如参数堆栈操作的时间,所以编译结果的运行代码会更快一些。 4、将数组作为参数传入函数,传的是引用而不是值。 九、函数指针的用法 使用函数指针的几种方法: void (*pfunc)(int); pfunc=callback_funcname; callback_funcname(1); 其中声明函数指针原型的代码可以在调用处写,也可以写成全局的。这种方法使用简单,适用于临时调用。 typedef void(*PFUNC)(int); PFUNC pfunc; pfunc=callback_funcname; callback_funcname(1); 这种方法适用于多次调用,先全局定义PFUNC,再在每个调用的地方声明临时变量后调用。 void (*MyClass::pfunc)(int); pfunc=&MyClass::callback_funcname; (this->*callback_funcname)(1); 和方法1类似,注意语法的不同。 typedef void(*PFUNC)(int); //在类中typedef PUNC pfunc; pfunc=&MyClass::callback_funcname; (this->*callback_funcname)(1); 和方法2类似,注意语法的不同。 十、typedef的不常用用法 typedef的一般用法是: typedef int UINT32; 但用来定义一个数组类型或指针函数时,比较特殊: typedef char CARRAY[32]; //定义了一个CARRAY的类型,代表char[32] typedef void(*PFUN)(int); //定义了一个指向指针函数的变量类型,函数原型为void xxx(int yyy); 十一、类的private/protected/public属性 1、类的成员如果没有指定访问域,默认是private的。 2、标识符protected 与 private类似,它们的唯一区别在继承时才表现出来。当定义一个子类的时候,基类的protected 成员可以被子类的其它成员所使用,然而private 成员就不可以。 3、public/protected/private继承的区别: 十二、关于空类 编译器为一个空类提供哪些默认函数? 1、C++编译器会提供默认的构造函数,析构函数, 拷贝构造函数和拷贝赋值操作符(请参考著名的Effective C++的第三版的第5条) sizeof一个空类等于多少? sizeof一个空类返回1。所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。C++编译器不允许对象为零长度。试想一个长度为0的对象在内存中怎么存放?怎么获取它的地址?为了避免这种情况,C++强制给这种类插入一个缺省成员,长度为1。如果有自定义的变量,变量将取代这个缺省成员。 十三、继承或多重继承情况下构造函数的调用顺序 (1)如果声明为Derive: public Super1,public Super2{AnotherClass m_obj;}; 构造函数的调用顺序是:Super1,Super2,AnotherClass,Derive. 析构函数的调用顺序应该是依次反过来的。 十四、虚函数、纯虚函数和抽象类、虚析构函数 虚函数的作用和运行原理 (1)多态是面向对象编程中的核心概念,就是说一个基类类型的指针实际上可能指向的是一个子类对象。只有在运行时才能根据实际情况来决定执行哪个函数,也就是动态联编。和动态联编对应的是静态联编,也就是说在编译时就决定了调用哪个函数。为了实现动态联编,必须将父类的函数声明为virtual。如果没有声明为virtual,可能得到的结果不是预期中的。 (2)对于包含了至少一个虚函数的类(或其父类包含虚函数),编译器需要为这个类增加4个字节,用来保存指向虚函数表VTABLE的指针。 纯虚函数和抽象类 包含了纯虚函数的类不能被直接实例化,可以称为抽象类。定义方法: 什么情况下需要指定析构函数为virtual? (1)析构函数不一定需要定义为虚函数,只有当这个类要作为其他类的父类使用时,才需要定义为虚函数。如果父类的析构函数没有定义为虚函数,则子类对象销毁时,父类析构函数不会被调用。 十五、多重继承情况下如何引用父类的同名成员? 重继承情况下,如果多个基类有同名成员,引用方法是: pDeriveObj->BaseClass1::Member; 十六、运算符重载 语法: Type Type::operator +(const Type &i){} 十七、友元 友元可以实现外部对private和protected成员的访问。有两种实现: 十八、模板 函数模板 实现语法: 在函数的声明和实现前加上template<typename T> ,(typename和class等价),可以写在一行里面,也可以分成两行写,注意>后面没有分号。如果声明和实现分开写,两个地方都要写上template<typename T>。 (1)一行代码中同时声明并实现函数 {...}; 引用语法: func<int>(5,6); 类模板 声明方法: 类模板可以实现在一个类中有一个通用类型的成员变量。 template <class T> class ClassName {public: T *m_pVariable; }; 引用方法: ClassName<int> obj; 模板特殊化 模板特殊化可以专门为某种数据类型定义特殊的行为。类的定义必须和通用的模板类完全一致,除了用专门语法,并将T修改为专门的类型,并定义特殊行为。 定义模板的默认值 模板的参数值 除了模板参数前面跟关键字class 或 typename 表示一个通用类型外,函数模板和类模板还可以包含其它不是代表一个类型的参数,例如代表一个常数,这些通常是基本数据类型的。 二十、类型转换和C++高级类型转换 基本类型强转有两种写法: int i; float f = 3.14; i = (int) f; i = int ( f ); 高级类型转换 ANSI-C++ 标准定义了4种新的类型转换操作符: reinterpret_cast,static_cast,dynamic_cast 和const_cast。 reinterpret_cast可以将一个指针转换为任意其它类型的指针。 static_cast可以执行所有能够隐含执行的类型转换,以及它们的反向操作(即使这种方向操作是不允许隐含执行的)。用于类的指针,也就是说,它允许将一个引申类的指针转换为其基类类型(这是可以被隐含执行的有效转换),同时也允许进行相反的转换:将一个基类转换为一个引申类类型。不会检查被转换的基类是否真正完全是目标类型的。 dynamic_cast 完全被用来进行指针的操作。它可以用来进行任何可以隐含进行的转换操作以及它们被用于多态类情况下的方向操作。然而与static_cast不同的是, dynamic_cast 会检查后一种情况的操作是否合法,也就是说它会检查类型转换操作是否会返回一个被要求类型的有效的完整的对象。 const_cast类型转换对常量const 进行设置或取消操作。 typeid (object_pointer) 二十一、命名空间 定义一个命名空间: namespace ns1{...} 设置默认命名空间: using namespace ns1; 引用其他命名空间的类型: ns2::variable = xx; 二十二、预处理命令 #undef 完成与 #define相反的工作,它取消对传入的参数的宏定义 #ifdef,#ifndef,#if,#endif,#else and #elif 指令#line 可以使我们对这两点进行控制,也就是说当出错时显示文件中的行数以及我们希望显示的文件名。它的格式是: 这个指令将中断编译过程并返回一个参数中定义的出错信息 这个指令是用来对编译器进行配置的,针对你所使用的平台和编译器而有所不同。 二十三、预定义宏 __LINE__ 整数值,表示当前正在编译的行在源文件中的行数。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |