随笔分类 - C++学习
摘要:1、_stdcall是Pascal方式清理C方式压栈,通常用于Win32 Api中,函数采用从右到左的压栈方式, 自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。int f(void *p) -->> _f@4(在外部汇编语言里可以用这个名字引用这个函数) 2、C调用约定(即用__cdecl关键字说明)(The C default calling convention)按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数vararg的函数(如pr
阅读全文
摘要:一、头文件(1)注意保护头文件,防止头文件被多重包含,所有的头文件都必须使用#ifndef、#define和#endif进行保护,书写格式如下<PROJECT>_<PATH>_<FILE>_H_,全部使用大写。例如:#ifndef FOO_H_,对应于foo.h头文件。(2)在头文件中尽量少的包含别的头文件,如果可以请使用前置声明代替包含头文件。包含过多的头文件将会产生较强的依赖性,一旦文件有所改变,将会导致所有文件的重新编译。因此,尽量少的包含别的头文件而是用前置声明。在头文件如何做到使用类Foo而无需访问类的定义?1) 将数据成员类型声明为Foo *或F
阅读全文
摘要:C++primer的解释是这样的: 变量的定义(definition):用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义; 变量的声明(declaration):用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。 以上解释讲得还是比较清楚的,有分配空间的叫定义,没分配空间的叫声明。好吧,你不知道什么时候分配什么时候没分配。让我们换一种说法。 “定义也是声明”,这说明声明包括定义。所以诸如int a;extern int a;之类的一定是声明。那是不是定义还要接着往下看; 如果
阅读全文
摘要:对象数组的构造:对象数据的构造一般有两种方式:静态和动态(1)静态分配 以string类为例,string a[10];就是以静态形式构造数据,这样的数组的个数是确定的不能修改的。 像这样的数组怎么进行构造和析构呢? 编译器在构造数组的时候会生成一个使用默认构造函数的数组构造函数arr_new(char *p,sizeof(string),int num,构造函数地址,析构函数地址);同样也会生成数组析构函数,形式类似。arr_del(char *p,sizeof(string),int num,析构函数地址); 若数组构造中间出现异常,该函数必须保证已构造的对象析构,然后释放内存。 ...
阅读全文
摘要:虚拟继承下的对象构造:由于虚拟基类对象在子类中只能保持一个实例,那么,子类构造的时候调用父类的构造函数的时候必须保证虚拟基类对象不能够重复构造。那么,C++规定虚拟基类对象的构造只能是最外层的子类进行构造,浅层次的子类将不会在进行构造,保证了虚拟基类对象的唯一性。在虚拟继承体系下,子类的构造函数中必须做一个判断,设置一个标准位,用来判断虚拟基类对象是否已经构建,然后将该标志为传递给浅层次的子类,那么虚拟基类将不会再次构造。例如,编译器会为子类构造函数内部设置标志位Point3D::Point3D(Point3D *this,bool _most_derived){ if(_most_deri.
阅读全文
摘要:类中函数的深度探索类中包含的函数主要有三种:static成员函数、nostatic成员函数、virtual成员函数。C++类中数据成员和成员函数的命名机制:数据成员的命名:在每个数据成员命名的时候编译器将该成员所属的类名也添加上,用来标志这个成员的来源范围。这样,继承类就可以与子类用相同的名字命名其成员,这样就不会产生二义性和冲突,但是对外界而言,通过继承类对象访问该名字,只会获取继承类的数据成员,因为根据命名查找机制,两个名字属于不同的作用域,继承类中的成员覆盖了子类中的成员,想要调用必须显示调用子类成员或在继承类作用域中使用using。(函数名相同)成员函数的命名:在同一个类中允许函数重载
阅读全文
摘要:在学习完类对象的构造后,下面就需要学习类数据成员和函数成员的存取。编译器对于类对象的处理方式:(1)对于空类,编译器为该类添加一个char类型的成员,用来唯一标识该类在内存的位置(2)使用对齐机制,当一个类的内存字节数不足4的倍数将自动补充,目的是为了寻址的方便有些编译器对于空类的处理进行了优化处理,仅当该空类被继承的时候,空类对象在子类对象中不占用任何内存,单独空类的大小仍是1个字节,这样可能会避免对齐机制,优化了C++对象模型的内存空间。但是对于非空类,不处理和优化处理对于类对象的内存空间没有任何改变。例如:class A{};class X:public virtual A{};clas
阅读全文
摘要:上一章讲过了关于类对象内存分布,对于nostatic数据将会放在对象内存空间中,static数据成员和nostatic、static函数成员将不会放在对象内存中,对于虚拟继承和含有虚函数的类来说,将会在对象内存中增加一个虚表指针,指向该类的虚表,其中虚表中将会存放虚函数的地址和虚拟基类的地址。一个类中只含有一个共享虚表(继承基类的虚表也是继承类的虚表,一般继承类的虚函数会存放在第一个基类的虚表中方便提取),对象中可以含有多个虚指针,继承至基类,除了直接虚拟继承的继承类才会产生新的vptr和虚表用于指示虚拟基类的位置。下面是如何构建类对象,即构造函数的深入探索。首先强调两个C++新手容易陷入的误
阅读全文
摘要:在实际生产中,遇到一个复杂的类,如果能看出这个类的内存模型结构,那么以后的操作基本就没有难度的;所以说,学会分析一个类的内存模型,是每一个C++程序员必须要会的知识。(1)C++类封装和C中的结构体的区别C++的类封装是在C语言中的结构体的基础上构建起来的,C结构体只允许存在数据,而不会存在对数据的操作。C++语言中延承C语言中的结构体,但增加的对数据的操作,即成员函数;类是对结构体的进一步封装,使某些数据成员对外不可见,称为私有成员。类和结构体最大的区别就是:结构体成员均是public类型的。那么,类和结构体的布局成本有没有区别呢?对于只有数据成员的类和结构体在内存的布局是相同的,没有增加成
阅读全文
摘要:http://blog.csdn.net/daheiantian/article/details/6530318异常的基本语法1. 抛出和捕获异常 很简单,抛出异常用throw,捕获用try……catch。 捕获异常时的注意事项: 1. catch子句中的异常说明符必须是完全类型,不可以为前置声明,因为你的异常处理中常常要访问异常类的成员。例外:只有你的catch子句使用指针或者引用接收参数,并且在catch子句内你不访问异常类的成员,那么你的catch子句的异常说明符才可以是前置声明的类型。 2. catch的匹配过程是找最先匹配的,不是最佳匹配。 3. catch的匹配过程中,对类型的要求
阅读全文
摘要:http://www.cnblogs.com/assemble8086/archive/2011/10/02/2198308.html有详细介绍,下面只讲什么时候模板类会实例化以及模板类中成员函数实例化和友元函数的使用。模板类的使用实现了编译时多态性,避免了运行时多态性的消耗(虚指针和虚函数表),但是编译时多态和运行时多态并不冲突,编译时多态是根据传入模板的对象类型实现不同的操作完成,比如继承同一接口的不同类型可以在编译时根据调用接口的队形类型实现多态,而运行时多态则根据虚函数来实现,必须等到运行时才能判断真正运行的接口。当模板类中使用虚函数时,模板实例化的时候必须实例化器虚函数表,一旦虚函数
阅读全文
摘要:在局部变量前加上“static”关键字,就成了静态局部变量。静态局部变量存放在内存的全局数据区。函数结束时,静态局部变量不会消失,每次该函数调用时,也不会为其重新分配空间。它始终驻留在全局数据区,直到程序运行结束。静态局部变量的初始化与全局变量类似.如果不为其显式初始化,则C++自动为其初始化为0。 静态局部变量与全局变量共享全局数据区,但静态局部变量只在定义它的函数中可见。静态局部变量与局部变量在存储位置上不同,使得其存在的时限也不同,导致对这两者操作 的运行结果也不同。对静态局部变量的说明:(1) 静态局部变量在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部.
阅读全文
摘要:一.多重继承#include <iostream>using namespace std;class Base1{public: Base1(){ cout << "Base1::Base1()" << endl;} virtual ~Base1(){ cout << "Base1::~Base1()" << endl;} virtual void speakClearly(){ cout << "Base1::speakClearly()" << e
阅读全文
摘要:C++中的引用与指针的区别指向不同类型的指针的区别在于指针类型可以知道编译器解释某个特定地址(指针指向的地址)中的内存内容及大小,而void*指针则只表示一个内存地址,编译器不能通过该指针所指向对象的类型和大小,因此想要通过void*指针操作对象必须进行类型转化。 ★ 相同点: 1. 都是地址的概念; 指针指向一块内存,它的内容是所指内存的地址; 引用是某块内存的别名。 ★ 区别: 1. 指针是一个实体,而引用仅是个别名; 2. 引用使用时无需解引用(*),指针需要解引用; 3. 引用只能在定义时被初始化一次,之后不可变;指针可变; 引用“从一而终” ^_^ 4. 引用...
阅读全文
摘要:C++指向函数的指针定义方式为:返回类型 (*指针名)(函数参数列表),例如 void (*p)(int)是指向一个返回值为void 参数为int类型的函数。而若想定义一个指向类成员函数的函数指针该怎么定义呢?对成员函数指针的使用。(1)非静态成员函数定义方式:返回类型 (类名::*指针名)(函数参数列表)例如void (A::*p)(int)是一个指向A类中成员函数的函数指针。赋值方式:p=&A::函数名,而一般的函数指针的赋值是p=函数名即可,注意区别。(成员函数必须是public类型的)调用方式:成员函数指针的调用必须通过类对象进行调用,a.*p(int)即可调用成员函数(该成员
阅读全文
摘要:虚函数表对C++了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。这里我们着重看一下这张虚函数表。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或
阅读全文
摘要:因为很多编译器在编译程序的过程是对程序进行优化,将程序中的变量值存储在缓存中以提高运行效率,这就可能出现这样的情况,用的到变量值不是最新的,而是已经存在在缓存中的值,这样就可能出现莫名其妙的错误,所以对一些易变的变量,加上violate 修饰,编译器在编译过程中就不会对读写这个变量就不会进行优化比如两个线程在运行中。第一个线程已经初始化,将变量a载入了缓存后,这时候第二个线程改变了a的值。如果a没有用volatile修饰的话,那么可能在第一个线程中使用a时,还是使用的缓存中没有改变过的值。(这只是一个可能性,因为多线程的运行谁都不能保证结果是什么,这是与不同的系统有关的)。如果使用了volat
阅读全文
摘要:在imperfect c++里,书里是这样解释POD的:1、 所有标量类型(基本类型和指针类型)、POD结构类型、POD联合类型、以及这几种类型的数组、const/volatile修饰的版本都是POD类型。2、 POD结构/联合类型:一个聚合体(包括class),它的非static成员都不是pointer to class member、 pointer to class member function、非POD结构、非POD联合,以及这些类型的数组、引用、const/ volatile修饰的版本; 并且,此聚合体不能有用户自定义的构造函数、析构函数、拷贝构造函数.POD结构(P...
阅读全文
浙公网安备 33010602011771号