一.静态成员
静态成员分为静态成员函数和静态数据成员,是为了实现我们有时候希望有某一个或者几个数据成员为类所定义出来的对象所共有,从而实现一个类多个对象之间的数据共享,如统计总数,平均值等。
静态数据成员:若类中的一个数据成员被声明为static,则这个成员为静态数据成员,这个成员为这个类的所有对象所共享。
我们需要注意以下几点:
1.静态数据成员与普通成员相类似,但在其前面要加上关键字static.
2.静态数据成员初始化应该在类外单独进行,一般在类对象定义之前。初始化的格式为:数据类型 类名::静态数据成员名=初始值;
3.静态数据成员属于类中对象的集合,而不像普通数据成员只属于类的某一个对象,可以用(类名::)访问静态数据成员;
4.静态数据成员与静态变量一样,是在编译创建并初始化,它在该类的任何对象被创建之前就存在,因此,公有的静态数据成员可以在对象定义之前被访问。对象定义后,公有的静态数据成员也可以通过对象进行访问。
静态成员函数:前面有static关键字说明的函数被称为静态成员函数,静态成员函数属于整个类,是该类所有对象所共有的成员函数,与静态数据成员不同的是,静态成员函数不是为了对象之间的沟通,而是为了处理静态数据成员。一般而言,静态成员函数不访问类中的非静态成员,若确实需要访问,可以通过对象名或引用或者是指针的方式访问非静态成员,想一下,为什么?
很简单,因为我们的静态成员函数不含this指针。
静态成员函数与非静态成员函数的重要区别是:非静态成员函数有this指针,而静态成员函数没有this指针,静态成员函数可以访问静态数据成员,因为静态数据成员是属于类的,可以访问。静态成员函数不能调用非静态成员函数,非静态成员函数可以调用静态成员函数。
关于静态成员函数的使用再做几点声明:
1.一般情况下,静态成员函数主要用来访问静态数据成员,当他与静态数据成员一起使用时,达到了对同一个类中对象之间共享数据的目的。
2.私有静态成员函数不能被类外面的函数和对象访问;
3.使用静态成员函数的一个原因是,可以用它在建立任何对象之前调用静态成员函数以处理静态数据成员,这是普通成员函数不能实现的功能。
4.编译系统将静态成员函数限定为内部连接,也就是说,与现行文件相连接的同名函数不会与该函数发生冲突,维护了该函数使用的安全性。
5.静态成员函数是类的一部分而不是对象的一部分。如果在类外调用公有的静态成员函数,可以用类名::静态成员函数名()方式调用。
关于静态成员我这里写一段代码大家简单理解一下:
#include<iostream> #include<string.h> using namespace std; class student { public: student(char* name="zhangsan",char* stu_no="0",float score=0)//析构函数 { _name=new char[strlen(name+1)]; strcpy(_name,name); _stu_no=new char[strlen(stu_no)+1]; strcpy(_stu_no,stu_no); _score=score; ++_count; _sum=_sum+score; _ave=_sum/_count; } ~student()//析构函数 { delete []_name; delete _stu_no; --_count; _sum=_sum-_score; } void show() { cout<<"姓名:"<<_name<<endl; cout<<"学号:"<<_stu_no<<endl; cout<<"成绩:"<<_score<<endl; //cout<<"学生人数"<<_count<<endl;可以访问静态成员 } static void show1() { cout<<"学生人数:"<<_count<<endl; cout<<"累加成绩:"<<_sum<<endl; cout<<"平均成绩:"<<_ave<<endl; //cout<<"姓名"<<_name<<endl;错,不能访问非静态成员 } private: char* _name; char* _stu_no; float _score; static int _count;//统计学生人数 static float _sum;//累加成绩 static float _ave;//平均成绩 }; int student::_count=0;//初始化静态数据成员 float student::_sum=0; float student::_ave=0; int main() { student a1("zhangyang","15060102132",100); a1.show(); student a("xjl","15060102133",100); a.show(); student::show1(); system("pause"); return 0; }
二.内联函数
为了消除C语言中宏定义的不安全性,C++中引入了内联,在函数名前加上关键字inline,该函数就被说明内联函数,在内联函数调用之时,编译器将函数体中的代码插入到调用该函数的语句之处,用实参取代形参,以便在程序运行之时不再对函数进行调用。
宏的优点:1.增强代码的复用性。
2.快,性能好。(在预处理期完成)
3.对于宏常量来说,可维护性更好
宏的缺点:
1.不方便调试。
2.对于宏函数来说,代码的可读性差,可维护性差,容易误用。
3.没有类型安全检查,因为宏与类型无关。
4.优先级问题和带副作用的宏可能引起不可预料的结果。
内联函数继承了宏的所有优点并且消除了不安全因素。内联函数没有像普通函数一样压栈的开销,提升了程序运行效率。
关于内联函数说明下面几点:
1.inline是一种用空间换时间的做法,省去的函数调用时的时间开销,但是当内联函数代码很长(循环或者递归)时不宜使用内联函数。
2.inline关键字和register关键字一样,加上对编译器来说只是一个建议,编译器通常会自动优化(在release下),如果内联函数体内有循环或者递归,编译器一般不会进行优化。
3.内联函数必须在声明和定义时前面都加上关键字inline,才能成为内联函数,仅将inline放在声明是不起作用的。
4.定义在类里面的成员函数默认为内联函数。
所以我们应当尽量用const ,enum,inline替代#define;
三.友元
友元可以分为友元函数和友元类。
友元函数既可以是不属于任何类的非成员函数,也可以是另一个类的成员函数,但它可以访问该类的所有成员,包括私有成员,保护成员和公有成员。友元函数用关键字friend说明。友元函数可以定义在类内部也可以定义在类外部。
关于友元函数的几点说明:
1.友元函数虽然可以访问类对象的私有成员,但是他毕竟不是成员函数,因此在定义友元函数时,不必加上类名::;
2.因为友元函数不是类的成员函数,所以它不能直接访问类的数据成员。它必需通过入口参数传递进来的对象名(对象指针或者对象引用)访问该对象的数据成员。
3.一个函数是一个类的友元后,这个函数可以访问类的私有成员,但在访问的时候,必须加上对象名。
4.友元函数提供了不同类的成员函数之间,类的成员函数与一般函数之间进行数据共享的机制,尤其当一个函数需要去访问对个类的时候,友元函数非常有用。多个类的友元函数可以访问相关所有类的数据。
我用日期类来实现一下友元函数的相关特性:
#include<iostream> using namespace std; class Date{ public: friend void show(const Date& d); private: int _year; int _month; int _day; }; void show(const Date& d)//可以定义在类内,也可以定义在类外,访问类成员加上对象名d { cout<<"year:"<<d._year<<endl; cout<<"month:"<<d._month<<endl; cout<<"day:"<<d._day<<endl; } int main() { Date d1; show(d1); system("pause"); return 0; }
在这里我顺便提一下输入输出运算符的重载,因为这里必须用友元函数去实现。
如果不用友元,代码如下:
ostream& operator<<(ostream& out) { out<<"year:"<<_year<<endl; out<<"month:"<<_month<<endl; out<<"day:"<<_day<<endl; return out; } istream& operator>>(istream& in) { cout<<"请输入年月日:"<<endl; in>>_year; in>>_month; in>>_day; return in; }
这里输入和输出运算符的重载都定义为成员函数,而这两个运算符的重载函数是具有this指针的,并且我们都知道this指针是第一个参数,这就导致了一个问题,我们在调用输入输出运算符重载函数的时候,必须这样来用:假设定义了对象Date d1;
d1<<cout;d1>>cin;这样的调用方式是不是有些不符合我们日常使用习惯呢。所以我们为了方便使用就必须消除掉默认的this指针,友元函数就是一个不错的选择。
完整代码如下:
#include<iostream> using namespace std; class Date{ public: friend ostream& operator<<(ostream& out,const Date& d); friend istream& operator>>(istream& in, Date& d);//不能声明为const, //要将标准输入中读取字符到对象中 private: int _year; int _month; int _day; }; ostream& operator<<(ostream& out,const Date& d) { out<<"year:"<<d._year<<endl; out<<"month:"<<d._month<<endl; out<<"day:"<<d._day<<endl; return out; } istream& operator>>(istream& in,Date& d) { cout<<"请输入年月日:"<<endl; in>>d._year; in>>d._month; in>>d._day; return in; } int main() { Date d1; cin>>d1; cout<<d1; system("pause"); return 0; }
运行结果:
友元类:整个类可以是另一个类的友元,则友元类的每一个成员函数都是另一个类的友元函数,都可以访问另一个类的保护成员或者私有成员。友元类的一般声明形式为:friend 类名;
#include<iostream> using namespace std; class Time{ public: friend class Date;//声明类date为它的友元类 private: int _hour; int _minute; int _second; }; class Date{ public: Date(int year,int month,int day,int hour,int minute,int second) { _year=year; _month=month; _day=day; _t._hour=hour; _t._minute=minute; _t._second=second; } void show() { cout<<"year:"<<_year<<endl; cout<<"month:"<<_month<<endl; cout<<"day:"<<_day<<endl; //可以访问类time的所有成员 cout<<"hour:"<<_t._hour<<endl; cout<<"minute:"<<_t._minute<<endl; cout<<"second:"<<_t._second<<endl; } private: int _year; int _month; int _day; Time _t; }; int main() { Date d(2018,3,29,9,47,2); d.show(); system("pause"); return 0; }
关于友元类的几点说明:
1、友元关系是单向的,不具有交换性,如上例中在类Time内声明类Date为它的友元类,则在Date类中可以访问类Time的所有成员,但是类Date却不是类Time的友元,在Time类中不能访问类Date的成员。
2.友元关系不具备传递性,如类x是类y的友元,类y是类z的友元,但是不能说明类x是类z的友元。
总结:友元提高了程序运行效率,实现了类之间的数据共享,但是友元在一定程度上破坏了数据的隐蔽性和封装性,降低了程序的可维护性,这与面向对象的设计思想背道而驰,所以友元不宜过多使用,能不用就尽量不用。
四.const成员函数
在成员函数后面使用关键字const说明的函数为常成员函数,const 修饰this指针所指向的对象,保证了调用const成员函数的对象在函数内不会被改变。const是函数类型的组成部分,因此在定义和声明的时候都要有关键字const进行说明。
#include<iostream> using namespace std; class Date{ public: Date(int year=1997,int month=1,int day=1) :_year(year) ,_month(month) ,_day(day) {}; void show() { //使用非const成员函数输出年月日 cout<<"year:"<<_year<<endl; cout<<"month:"<<_month<<endl; cout<<"day:"<<_day<<endl; } void show()const { //使用const成员函数输出年月日 cout<<"year:"<<_year<<endl; cout<<"month:"<<_month<<endl; cout<<"day:"<<_day<<endl; //_year=10;错误,不能被修改 } private: int _year; int _month; int _day; }; int main() { Date d(2017,11,25); d.show(); const Date d1(2018,3,29); d1.show(); system("pause"); return 0; }
关键字const可用于重载函数进行区分;
const对象不能调用非const成员函数,可以调用const成员函数。
非const对象可以调用非const成员函数和const成员函数。
const成员函数内可以调用其他的const成员函数,不可以调用非const成员函数。
非const 成员函数内可以调用其他的const成员函数和非const成员函数。