静态成员变量必须要在类外初始化
type class::name = value;初始化的时候不用带static但需要有类型,PRotected,privated,public都可以被这样初始化
静态成员变量的调用
//通过类类访问 static 成员变量Student::m_total = 10;//通过对象来访问 static 成员变量Student stu("小明", 15, 92.5f);stu.m_total = 20;//通过对象指针来访问 static 成员变量Student *pstu = new Student("李华", 16, 96);pstu -> m_total = 20;static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。静态成员变量初始化时可以赋初值,也可以不赋值。如果不赋值,那么会被默认初始化为 0。全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的,一般认为是垃圾值。静态成员变量既可以通过对象名访问,也可以通过类名访问,但要遵循 private、protected 和 public 关键字的访问权限限制。当通过对象名访问时,对于不同的对象,访问的是同一份内存。静态成员变量可以成为派生类和基类共同使用的数值,也可以成为成员函数的可选参数。静态成员变量可以是所属类的类型,普通数据成员只能成为该类的指针或引用。静态成员函数的地址可以用普通函数指针调用
class A{public: static fun(){}; int fun1(){};}int (*pf1)()=&base::fun;int (base::*pf2)()=&case::fun1;静态成员函数不可以调用类的非静态成员,因为这个静态成员函数并不带有this指针。静态成员函数不可以同时声明为 virtual const volatile函数。静态成员函数不需要对象名即可调用。非静态成员函数可以自由调用静态成员函数和静态成员变量。调用时机
类作为一个参数整体传入一个函数的时候,需要调用这个类的拷贝构造函数,进行形参和实参的复制类作为一个结果返回的时候,先产生一个临时变量,调用拷贝构造函数将返回值拷贝到临时变量,析构返回的变量,再析构临时变量需要通过另一个变量初始化的时候
class mode{...}mode A(10);mode B = A;拷贝构造函数分为浅拷贝和深拷贝。默认拷贝构造函数是浅拷贝的一种
默认拷贝构造函数无法处理静态成员变量只是简单复制需要自己写浅拷贝构造函数进行静态成员变量的复制如果被拷贝对象中包含指针,进行逐位拷贝后新旧两个指针将指向同一个空间,并且将被重复释放深拷贝用于需要动态创建新空间时
Rect(const Rect& r) { width = r.width; height = r.height; p = new int; // 为新对象重新动态分配空间 *p = *(r.p); } 可以创建一个private的拷贝构造函数声明来解决默认值拷贝。???空初始化:即无参数无括号形式
如int i,new int,new int[10].当在所有函数之外时,初始化为0;当在某一函数中时,没初始化。值初始化:即无参数有括号形式,且括号只能在类型名后,而不能在变量名之后,即只能创无名对象,对象被值初始化为0.
如:int() //创建了一个无名对象,其被值初始化为0.一般将该无名对象初始化化或赋值给某有名对象,或直接作为无名对象使用显式初始化:即有参数有括号形式,且当为有名对象时括号在对象名之后,为无名对象时括号在类类型名之后。
如:int i(5); new int(5);以下四种必须使用初始化列表: 初始化一个引用成员变量初始化一个const变量当我们在初始化一个子类对象的时候,而这个子类对象的父类有一个显示的带有参数的构造函数当调用一个类类型成员的构造函数,而它拥有一组参数的时候析构函数通常使用默认析构函数,但是在之前进行空间改变(指针移位等)的时候一定要自己写析构函数。析构数组或类组:
class A{ A(){m_a=new int[10];} ~A(){delete [] m_a;} int * m_a;}强制类型转换支持但并不推荐,推荐使用以下较温和的方法:
pd = static_cast<double*>(pv);初始化列表不管怎么写,初始化的顺序也只是按照原类内声明的顺序进行。两种重载方式的比较:
一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
类型转换函数只能定义为一个类的成员函数而不能定义为类的友元函数。 C++提供4个类型转换函数:reinterpret_cast(在编译期间实现转换)、const_cast(在编译期间实现转换)、stactic_cast(在编译期间实现转换)、dynamic_cast(在运行期间实现转换,并可以返回转换成功与否的标志)。
若一个运算符的操作需要修改对象的状态,选择重载为成员函数较好。
若运算符所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则只能选用友元函数。 当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是对该类对象的引用)。如果左边的操作数必须是一个不同类的对象,或者是一个内部 类型的对象,该运算符函数必须作为一个友元函数来实现。
当需要重载运算符具有可交换性时,选择重载为友元函数。
注意事项:
除了类属关系运算符”.“、成员指针运算符”.*“、作用域运算符”::“、sizeof运算符和三目运算符”?:“以外,C++中的所有运算符都可以重载。
重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。 运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
继承
class Human{ …};class Man : public Human{ …};class Boy : public Man{ …};组合
class Eye{ public: void Look(void);};class Nose{ public: void Smell(void);};class Mouth{ public: void Eat(void);};class Ear{ public: void Listen(void);};class Head{ public: void Look(void) { m_eye.Look(); } void Smell(void) { m_nose.Smell(); } void Eat(void) { m_mouth.Eat(); } void Listen(void) { m_ear.Listen(); } private: Eye m_eye; Nose m_nose; Mouth m_mouth; Ear m_ear;};继承的关系不同对这个派生类并无影响,而是对该派生类的派生类产生影响。例如private Base(10),则对于该派生类的派生类来说,Base不可见。纯虚函数:只声明,无定义,包含纯虚函数的类称为抽象类,无实际作用,只作为基类。
class <类名>{virtual <类型><函数名>(<参数表>)=0;…};重载和覆盖的区别
重载的几个函数必须在同一个类中; 覆盖的函数必须在有继承关系的不同的类中覆盖的几个函数必须函数名、参数、返回值都相同; 重载的函数必须函数名相同,参数不同。覆盖的函数前必须加关键字Virtual; 重载和Virtual没有任何瓜葛,加不加都不影响重载的运作。关于C++的隐藏规则:
如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。重写 重载 重定义
重写(override): 父类与子类之间的多态性。子类重新定义父类中有相同名称和参数的虚函数。
1) 被重写的函数不能是 static 的。必须是 virtual 的 ( 即函数在最原始的基类中被声明为 virtual ) 。
2) 重写函数必须有相同的类型,名称和参数列表 (即相同的函数原型)
3) 重写函数的访问修饰符可以不同。尽管 virtual 是 private 的,派生类中重写改写为 public,protected 也是可以的
重载 (overload): 指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。
重定义 (redefining): 子类重新定义父类中有相同名称的非虚函数 ( 参数列表可以不同 ) 。
重写与重载的区别 (override) PK (overload)
方法的重写是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关 系,是水平关系。
重写要求参数列表相同;重载要求参数列表不同。
重写关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
对象的动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。
D* pD = new D();//pD的静态类型是它声明的类型D*,动态类型也是D* B* pB = pD;//pB的静态类型是它声明的类型B*,动态类型是pB所指向的对象pD的类型D* C* pC = new C(); pB = pC;//pB的动态类型是可以更改的,现在它的动态类型是C* 静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。只有虚函数是动态绑定,其余函数都是静态绑定。动态绑定的函数调用的函数体看实际上的对象类型,静态绑定的函数调用的函数体看声明的对象类型。虚函数是动态绑定的,但是为了执行效率,缺省参数是静态绑定的。对于一个使用了虚函数的基类来说:
Base b = d;//直接赋值(产生切割) b.Test(); Base& b2 = d;//使用引用赋值(不产生切割) b2.Test(); Base* b3 = &d;//使用指针赋值(不产生切割) b3->Test(); //覆盖方法和子类数据丢失的现象生成切割(slice)模板的一般形式:
Template <class或者也可以用typename T>返回类型 函数名(形参表){//函数定义体 }//template是一个声明模板的关键字,表示声明一个模板关键字class不能省略,如果类型形参多余一个 ,每个形参前都要加class <类型 形参表>可以包含基本数据类型可以包含类类型.template <class T> inline T square(T x) { T result; result = x * x; return result; };http://www.VEVb.com/ggjucheng/archive/2011/12/18/2292089.html
explicit可以避免隐式调用构造函数。
新闻热点
疑难解答