深度剖析C++第二部分
1、通过对象名能够访问public成员变量。每个对象的成员变量都是专属的,成员变量不能够在对象之间共享。
2、需求:统计在程序运行期间某个类的对象数目,保证程序的安全性(不能使用全局变量),随时可以获取当前对象的数目。
(1)使用全局变量的情况
- #include <iostream.h>
- #include <stdio.h>
- int count;
- int getCount()
- {
- return count;
- }
- class Test{
- public:
- Test()
- {
- count++;
- }
- ~Test()
- {
- --count;
- }
- };
- Test t0;
- Test t1;
- int main(int argc, char *argv[])
- {
- Test t;
- printf("count:%d ",getCount());
- return 0;
- }
(2)使用static变量的情况
- #include <iostream.h>
- #include <stdio.h>
- class Test{
- private:
- static int count; //静态成员变量
- public:
- Test()
- {
- count++;
- }
- ~Test()
- {
- --count;
- }
- static int getCount() //静态成员函数
- {
- //printf("count:%d ",count);
- return count;
- }
- };
- int Test::count=0; //类的静态成员变量必须先初始化再使用 另外占用空间(全局数据区空间)
- Test t0;
- Test t1;
- int main(int argc, char *argv[])
- {
- Test t;
- printf("count:%d ",Test::getCount());
- //t.getCount();
- return 0;
- }
3、关于类的静态成员变量,通常情况下都是定义为public的,因为静态成员变量的生命周期不依赖于任何一个对象,静态成员变量属于整个类所有,所有对象共享类的静态成员变量,我们可以通过类名直接访问公有静态成员变量,也可以通过对象名访问公有静态成员变量。
因为静态成员变量不属于某一个特定的对象所有,所以他不能分配在 对象空间上,因此它需要在类外单独分配空间,它的存储在全局数据区。静态成员变量的生命期为程序运行期。类中可以通过static关键字定义静态成员变量。
在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。所以在所有对象中都可以共享它。使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存。
不能通过类名来调用类的非静态成员函数,只能调用类的静态成员函数和静态成员变量。类的对象可以使用静态成员函数和非静态成员函数。静态成员函数中不能引用非静态成员(因为静态成员函数属于整个类,在类实例化对象之前就已经分配空间了,而类的非静态成员必须在类实例化对象后才有内存空间,所以这个调用就出错了,就好比没有声明一个变量却提前使用它一样。);类的非静态成员函数可以调用用静态成员函数,但反之不能。类的静态成员变量必须先初始化再使用。
对类的静态成员变量和成员函数作个总结:
一。静态成员函数中不能调用非静态成员。
二。非静态成员函数中可以调用静态成员。因为静态成员属于类本身,在类的对象产生之前就已经存在了,所以在非静态成员函数中是可以调用静态成员的。
三。静态成员变量使用前必须先初始化(如int MyClass::m_nNumber = 0;),否则会在linker时出错。
参考博客:http://blog.csdn.net/morewindows/article/details/6721430
静态成员函数只能访问静态成员变量或静态成员函数,而不能访问普通成员函数,因为静态成员函数在对象构造前就已经分配 了存储空间。
静态成员函数可以通过类名直接访问,也可以通过对象名直接访问,静态成员函数没有隐藏的this参数。
4、二阶构造模式 参考博客:http://blog.csdn.net/lgc_lg/article/details/54783242
构造函数中可以使用return语句,构造函数执行结束不一定说明对象就构造成功了。构造函数只是决定对象的初始状态,而不能决定对象的诞生。半成品对象:构造函数的初始化过程不能按照预期的计划完成构造而形成的对象,半成品对象也是合法的c++对象,但也是BUG的重要来源。构造函数中初始化操作失败不影响对象的诞生。
二阶构造是人为的将初始化过程分为两个部分,二阶构造的根本意义在于保证创建的 对象都是完整初始化的,不存在半成品对象。
工程开发中的构造过程分为:
(1)资源无关的初始化操作:不可能出现异常情况的操作
(2)需要使用系统资源的操作:可能出现异常情况:如 :内存申请、文件访问、定义指针等
程序代码示例:
- #include <stdio.h>
- class TwoPhaseCons
- {
- private:
- TwoPhaseCons() // 第一阶段构造函数
- {
- }
- bool construct() // 第二阶段构造函数
- {
- return true;
- }
- public:
- static TwoPhaseCons* NewInstance(); // 对象创建函数
- };
- TwoPhaseCons* TwoPhaseCons::NewInstance()
- {
- TwoPhaseCons* ret = new TwoPhaseCons();
- // 若第二阶段构造失败,返回 NULL
- if( !(ret && ret->construct()) )
- {
- delete ret;
- ret = NULL;
- }
- return ret;
- }
- int main()
- {
- TwoPhaseCons* obj = TwoPhaseCons::NewInstance();
- printf("obj = %p ", obj);
- delete obj;
- return 0;
- }
5、友元关系发生在函数与类之间或者类与类之间,友元关系是单项的,不能传递。在类中以friend关键字来声明友元,类的友元可以是其他类或者具体函数。友元不是类的一部分,友元不受类中访问级别的限制,友元可以访问具体类的所有成员。在类中用friend关键字对函数或类进行声明。
友元关系不具备传递性,类的友元可以是其他类的成员函数,也可以是某个完整的类。
代码示例一:类与类之间作为友元
- #include<iostream>
- #include<stdio.h>
- class TestA{
- private: friend class TestB;
- int j;
- public:
- TestA()
- {
- j=11;
- }
- };
- class TestB{
- private: friend class TestC;
- int i;
- public:
- TestB()
- {
- i=10;
- }
- /* int get(TestA obj) 友元不具有传递性
- {
- return obj.j;
- }
- */
- };
- class TestC{
- public:
- int get (TestB obj)
- {
- return obj.i;
- }
- int get (TestA obj)
- {
- return obj.j;
- }
- };
- int main()
- {
- TestC c;
- TestB b;
- TestA a;
- printf("c.get(b)=%d ",c.get(b));
- printf("b.get(a)=%d ",b.get(a));
- //printf("%d ",c.get(a)); error 友元在类之间不具有传递性
- return 0;
- }
代码示例二:成员函数作为一个类的友元
- #include<iostream>
- #include<stdio.h>
- class TestA{
- private:
- int j;
- public:
- TestA()
- {
- j=11;
- }
- friend int getJ(TestA obj); //友元声明 函数作为一个类的友元
- };
- int getJ(TestA obj)
- {
- return obj.j;
- }
- int main()
- {
- TestA a;
- printf("%d ",getJ(a));
- return 0;
- }
代码示例三:
代码从示例四:
6、函数重载的本质为相互独立的不同函数,c++中通过函数名和函数参数确定函数调用,所以说无法直接通过函数名得到重载函数的入口地址,函数重载必然发生在同一个作用域中。(类作用域、全局作用域、局部作用域等) 全局函数和成员函数是不能够进行重载关系的。
构造函数的重载 普通成员函数的重载 静态成员函数的重载
函数重载的意义:(1)扩展系统中已经存在的函数功能(2)扩展系统中不存在的功能
7、操作符重载
c++中的重载能够扩展操作符的功能,操作符的重载以函数的方式进行,本质是用特殊形式的函数扩展操作符的功能。
通过operator关键字可以定义特殊的函数,operator的本质是通过函数重载操作符
操作符重载遵循相同的函数重载规则 ,全局函数和成员函数都可以实现对操作符的重载。