C++学习小记

纯虚函数

纯虚函数是在声明虚函数时被“初始化”为0的函数。声明纯虚函数的一般形式是 virtual 函数类型 函数名 (参数表列) =0; 注意: ①纯虚函数没有函数体;②最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”; ③这是一个声明语句,最后应有分号。 纯虚函数只有函数的名字而不具备函数的功能,不能被调用。它只是通知编译系统: “在这里声明一个虚函数,留待派生类中定义”。在派生类中对此函数提供定义后,它才能具备函数的功能,可被调用。

C++中 的虚函数的作用主要是实现了多态的机制。而虚函数是通过虚函数表(V-Table)实现的。
构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。

包含至少一个纯虚函数的类视为抽象类

构造函数

只要实例化对象时不需要参数的构造函数都属于默认构造函数
构造函数为什么不能声明为虚函数?
1 构造一个对象的时候,必须知道对象的实际类型,而虚函数行为是在运行期间确定实际类型的。而在构造一个对象时,由于对象还未构造成功。编译器无法知道对象的实际类型,是该类本身,还是该类的一个派生类,或是更深层次的派生类。无法确定。
2 虚函数的执行依赖于虚函数表。而虚函数表在构造函数中进行初始化工作,即初始化vptr,让他指向正确的虚函数表。而在构造对象期间,虚函数表还没有被初 始化,将无法进行。 析构函数执行时先调用派生类的析构函数,其次才调用基类的析构函数。

在没有const限制的前提下,自己类的成员函数访问自己的类的变量是允许的,你的问题是初始化列表使用错误

除了构造函数初始化成员变量或私有变量是const修饰的时候,普通函数不能用初始化列表来赋值的,把赋值过程改到函数体里就可以了

析构函数

一般情况下类的析构函数里面都是释放内存资源,而析构函数不被调用的话就会造成内存泄漏。我想所有的C++程序员都知道这样的危险性。当然,如果在析构函数中做了其他工作的话,那你的所有努力也都是白费力气。
将基类的析构函数写成虚函数,这样做是为了当用一个基类的指针删除一个派生类的对象时,派生类的析构函数会被调用。

虚函数

在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数

多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。

多态还有个关键之处就是一切用指向基类的指针或引用来操作对象。

只需在把基类的成员函数设为virtual,其派生类的相应的函数也会自动变为虚函数。那么对于在派生类的相应函数前是否需要用virtual关键字修饰,那就是你自己的问题了(语法上可加可不加,不加的话编译器会自动加上,但为了阅读方便和规范性,建议加上)。

指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。

空结构体和空类

空结构体内存占用大小为1.这个隐晦的1是编译器安插进去的一个char,使得2个空的struct或者class在内存中具有独一无二的地址。
++语言中的确规定了空结构体和空类所占内存大小为1,C语言中空类和空结构体占用的大小是0(gcc编译器),C++规定任何不同的对象不能拥有相同的内存地址,如果空类对象大小为0,那么此类数组中的各个对象的地址会一致。C++编译器会在空类或空结构体中增加一个虚设的字节(有的编译器可能不止一个),以确保不同的对象都具有不同的地址。C++的选择是,自动的给空类插入一个char类型(只一个特殊对待),只要这个类对象将来占空间,那么就可以通过地址来区分他们。

我们利用类型(通常是空类)来区别对待不同类对象的属性。(其实我们是可以通过使用常数来区分的,但是区别我们很容易就能知道)。

使用常数来区分需要使用if else的这种运行时来确定执行的线路的方法,而使用函数重载的方法,在参数中加入一个空类域作为区分不同的函数的方法,编译的时候直接选择,而不是在运行的时候选择,这是非常提高效率的。

要知道,不同的空类,是不同的。他们代表着不同的类型(虽然他们结构一样)。在STL中,使用空类区分不同类型的标志,从而在编译的时候来对不同的类进行有针对性的优化是非常常见的。

#pragma 宏

#pragma 作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。#pragma once是一个比较常用的C/C++杂注,只要在头文件的最开始加入这条杂注,就能够保证头文件只被编译一次

在函数前后增加 #pragma optimize,发现可以针对函数进行优化。

#ifdef #endif

条件编译
条件编译,满足一定条件时才编译

在头文件中使用#ifdef和#ifndef是非常重要的,可以防止双重定义的错误。

你在头文件aaa.h中定义了一个类aaa如下:

 class   aaa   

  {   

  };   

如果两次#include “aaa.h”(不见得是直接,也有可能两个不同的头文件中都包含了这个头文件)就会出错,因为相同的类不能定义两次。把aaa.h稍做修改:

#ifndef   aaa

#define   aaa

  class   aaa   

  {   

  };   

#endif

就可以避免这样的问题。因为当你已经包含过这个文件,aaa就会有了定义,那么#ifndef的条件为假,就不会再执行后面的类定义了。

#ifdef#endif必须成对使用。

从理论上讲可以出现在任何地方(头文件和实现文件中)

通常为了防止头文件被多次包含,在头文件中使用是必须的:

如:#ifndef MY_HEAD_H //头文件开头,名字是任意的,注意不要和其它头文件冲突

头文件声明

#endif //头文件结尾

有时候,在b.h中会include “a.h” ,在”c.h”中会include “b.h”及include”a.h”, 这时,如果不用ifndef/endif,就会包含两次a.h,产生错误。

extern”C”

extern “C”指令非常有用,因为C和C++的近亲关系。注意:extern “C”指令中的C,表示的一种编译和连接规约,而不是一种语言。C表示符合C语言的编译和连接规约的任何语言,如Fortran、assembler等。

还有要说明的是,extern “C”指令仅指定编译和连接规约,但不影响语义。例如在函数声明中,指定了extern “C”,仍然要遵守C++的类型检测、参数转换规则。

再看下面的一个例子,为了声明一个变量而不是定义一个变量,你必须在声明时指定extern关键字,但是当你又加上了”C”,它不会改变语义,但是会改变它的编译和连接方式。

如果你有很多语言要加上extern “C”,你可以将它们放到extern “C”{ }中。

#define __delscpec(dllimport)

不使用__ declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。

auto c++ 11 新特性

auto主要有两种用途:自动类型推断和返回值占位。auto在C++98中的标识临时变量的语义,由于使用极少且多余,在C++11中已被删除。前后两个标准的auto,完全是两个概念。

NULL 和 nullptr

C和C++中的NULL不等价

在C中,习惯将NULL定义为void*指针值0:#define NULL (void*)0

但同时,也允许将NULL定义为整常数0

在C++中,NULL却被明确定义为整常数0: #define NULL 0

原因是因为隐式类型转换

C++不支持void*到其他类型的隐式转换,C支持void*到0的隐式类型转换,宏观表现则是,void*指针可以直接赋值给其他的指针类型

为什么C++在NULL上选择不完全兼容C?原因和C++的重载函数有关。

C++通过搜索匹配参数的机制,试图找到最佳匹配(best-match)的函数,而如果继续支持void*的隐式类型转换,则会带来语义二义性(syntax ambiguous)的问题。

但C++这么做又带来了另一个问题:NULL和整型发生了重叠。

为了彻底解决NULL带来的问题,c++11标准引入了一个新的关键字:nullptr。

nullptr是一个很神奇的关键字,他被定义为std::nullptr_t类型,这是一个泛指针类型(这个术语是我自己加的-.-),支持到其他指针类型的自动转换。并且不支持到除了bool类型以外的整型的转换。

由于nullptr具有类型,且支持到任何兼容的指针类型的转换,所以对于上述重载函数的调用,编译器会很明确的选择第二个函数。

不过,引入新关键字并不代表解决了一切问题。其代价也很明显:向后兼容性/习惯性和编译器的支持程度。

uint32_t和uint32

uint32_t is standard, uint32is not. That is, if you include or , you will get a definition of uint32_t. uint32 is a typedef in some local code base, but you should not expect it to exist unless you define it yourself. And defining it yourself is a bad idea.

_MSC_VER宏

MSC_VER是微软公司推出的C/C++编译器在ANSI/ISO C99标准之外扩展的宏定义,用来定义当前微软公司自己的编译器的主版本。需要注意的是,这并不是Visual Studio 的版本号,也不是Visual C++的版本号。如Visual Studio 2005的Vistual C++版本为8.0,所附带编译器的MSC_VER定义是1400;目前最新的Visual Studio 2015的Visual C++版本为14.0,相应_MSC_VER为1900

iterator的溢出问题

使用iterator,end指向的不是最后一个元素,而是最后一个元素后面那个,使用iterator,要防止越界,如果不是迭代器递增1的话,使用for循环,读取容器的大小来控制不要溢出。while(it!= iterator.end()){it+=2}这种会越界.

error

一般出现lnk2019错误都是库文件没添加造成的

使用sizeof计算类的大小

类的sizeof大小一般是类中的所有成员的sizeof大小之和,这个就不用多说。
不过有两点需要注意:1)当类中含有虚成员函数的时候,例如:
class B
{
float a;
public:
virtual void fun(void);
}
此时sizeof(B)的大小为8,而不是4。因为在类中隐藏了一个指针,该指针指向虚函数表,正因为如此,
使得C++能够支持多态,即在运行时绑定函数的地址。
2)另一个要注意的是,当类中没有任何成员变量,也没有虚函数的时候,该类的大小是多少呢?
例如:
class B2
{
void fun(void);
}
此时sizeof(B2)的值是多少呢?在C++早期的编译器中,这个值为0;然而当创建这样的对象时,
它们与紧接着它们后面的对象有相同的地址。比如:
B2 b2;
int a;
那么对象b2与变量a有相同的地址,这样的话对对象b2地址的操作就会影响变量a。所以在现在大多数编译器中,该值的大小为1。

如果有虚函数,则sizeof值为类的数据成员的大小加上VTBL(指针,4字节),再加上其基类的数据成员的大小。如果是多重继承,还得加上各基类的VTBL。

vector拷贝的三种方式

  1. 初始化构造 vector v1(v2);
  2. swap赋值 vector v1(); v1.swap(v2); // v1 = v2
  3. assign赋值 v1.assign(v2.begin(), v2.end());
  4. 使用迭代器循环语句赋值 (效率较差)

C++ 默认带形参值的函数

http://www.cnblogs.com/jiu0821/p/4750316.html

全局变量&静态变量

http://www.cnblogs.com/lebronjames/archive/2013/05/22/3093064.html
http://www.letuknowit.com/archives/86/

LINUX时间机制

http://blog.sina.com.cn/s/blog_790f5ae10100rwd3.html

函数的默认参数注意事项

1.调用函数的时候可以省略默认参数项,使用默认值的参数不写。
2.默认参数的值也可以是表达式,函数调用时表达式进行求值。
3.默认参数有连续性,如果一个参数是默认参数,后面的参数也必须是默认参数。
4.默认参数可以放在函数声明或者定义中,但只能放在二者之一,为了提高函数的重用性,最好还是放在函数声明中。
5.函数重载的时候应该非常谨慎的使用默认参数,不要遵循第一条不写使用默认值的参数,会使编译器混淆。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值