一、友元:
1. 友元函数 友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不 属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
友元函数并不是类中的一个成员
(1)注意:
a、友元函数可访问类的私有成员,但不是类的成员函数
b、友元函数不能用const修饰
c、友元函数可以在类定义的任何地方声明(除了在函数体内),不受类访问限定符限制(不是类的成员函数)
d、一个函数可以是多个类的友元函数
class Date
{
friend ostream& operator<<(ostream& _cout, const Date &d);
public:
Date(int year = 2943, int month = 243,int day = 2)
:_year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
};
class Time
{
friend ostream& operator<<(ostream& _cout, const Date &d);//将日期内的友元声明在时间内的里面,说明,可以在日期内里面调用时间内的成员
public:
Time(int hour, int minute, int second)
:_hour(hour)
, _minute(minute)
, _second(second)
{
}
private:
int _hour;
int _minute;
int _second;
};
ostream &operator<<(ostream &_cout, const Date&d)//此时cout作为左操作数,对象中的内容作为右操作数
{
Time t(12,3,4);
_cout << t._hour << "/" << t._minute << "/" << t._second;
_cout << d._year << "/" << d._month << "/" << d._day;
return _cout;//cout的生命周期比函数长,需要返回引用
}
void TestDate()
{
Date d1(234, 5, 6);
Date d2(d1);
Date d3;
cout << d3 << endl;
}
e、友元函数的调用与普通函数的调用和原理相同(友元函数不是类的成员函数,不需要通过对象来初始化,把对应的参数个数传过来即可。类的成员函数必须根据类的对象来进行调用。成员函数的参数比实际写出来的参数多一个
2、友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个 类中的非公有成员
3、优缺点:
a、优点:提高了程序运行效率(节省了函数调用和返回时的时间开销)
b、缺点:破坏了类的封装性和隐藏性(使类中给定的访问限定符如private不起作用,类中私有的成员在类外也可以访问,可以在友元函数中直接使用。)
4、a、友元关系不能继承
两个文件中定义两个类,互相包含(会引起头文件的重复包含),引用头文件或者从外部声明都会产生错误,我们可以把类中创建的另一个类对应的对象声明为一个指针,动态申请空间。如下:
Time.h中
class Date;
class Time
{
friend Date;
public:
Time(int hour, int minute, int second);
private:
int _hour;
int _minute;
int _second;
Date *_d;
};
Time.cpp中:
#include"Time.h"
#include"Date.h"
#include<malloc.h>
Time::Time(int hour, int minute, int second)
:_hour(hour)
, _minute(minute)
, _second(second)
{
_d = (Date*)malloc(sizeof(Date));
}
Date.h中:
class Time;
class Date
{
public:
Date(int year = 2943, int month = 243,int day = 2);
void PrintDate();
private:
int _year;
int _month;
int _day;
Time *_t;//看不到Time类,并不知道该类占几个字节,但是指针在32位平台下占4个字节。
};
在日期类的源文件动态从堆上申请一个对象中,把指针变为对象
Date.cpp中:
# include"Date.h"
#include"Time.h"
#include<malloc.h>
#include<iostream>
using namespace std;
Date::Date(int year = 2943, int month = 243,int day = 2)
:_year(year)
, _month(month)
, _day(day)
{
_t=(Time*)malloc(sizeof(Time));//指针指向对象
}
void Date::PrintDate()
{
cout<<_year<<"/"<<_month<<"/"<<_day<<endl;
cout<<_t->_hour<<_t->_minute<<_t->_second<<endl;
}
可以在时间类中访问日期类的私有成员变量;若想在日期类中访问时间类的成员变量,则在日期类中声明“friend Time;",让日期类成为时间类的友元。
b、友元关系是单向的,不具有交换性
c、友元关系不能传递
一个类创建对象的个数?
a、count定义为局部变量,出了作用域函数就销毁,如下代码,结果为2,销毁只剩下主函数中创建的两个对象。
class Date
{
//只有构造函数,拷贝构造函数才有可能创建对象
//只有构造函数,拷贝构造函数和销毁对象时,个数才可能发生变化
public:
Date()
:_count(0)
{
_count++;
}
Date(Date& d)
:_count(d._count++)//用d中的count把当前对象中的count拷贝出来
{
_count++;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
int _count;//计录创建对象的个数,都为整型
};
void TestDate()
{
Date d3;
Date d4(d3);
}
int main()
{
Date d1;
Date d2(d1);
TestDate();
return 0;
}
b、count定义为全局变量出了函数的作用域,会销毁,count变为0的情况(全局变量记录个数,每个对象可以共享,和对象没有任何关系)因为每个成员函数中都有,可以将其抽离出来,变为共享的。
int _count = 0;//共享的创建全局变量
class Date
{
//只有构造函数,拷贝构造函数才有可能创建对象
//只有构造函数,拷贝构造函数和销毁对象时,个数才可能发生变化
public:
Date()
:_count(0)
{
_count++;
}
Date(Date& d)
:_count(d._count++)//用d中的count把当前对象中的count拷贝出来
{
_count++;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
int _count;//计录创建对象的个数,都为整型
};
void TestDate()
{
}
int main()
{
Date d1;
Date d2(d1);
Date d3;
Date d4(d3);
TestDate();
return 0;
}
全局变量可以放到局部范围内修改,一修改就会导致结果不准确。使用全局变量不安全。
void TestDate()
{
_count = 0;
Date d3;
Date d4(d3);
}
int main()
{
Date d1;
Date d2(d1);
TestDate();
return 0;
}
执行到TestDate函数中的_count=0时,会把计数的结果变为0。
把计数的变量包含到成员变量中,每个成员函数都可以引用,也会导致统计结果不准确。
完善以上代码,既需要一个整型的成员变量,又需要是被共享的(包含到每一个类对象中去),还需要这个变量在类对象中访问不了。采用static静态成员变量。
二、静态变量
声明为static的类成员(成员数据或成员函数)称为类的静态成员 ,静态成员变量必须先拿到类外进行定义,类中只相当于一个声明,编译器不会开辟空间。定义时必须和类有一定的关系。
1、特点:
a、静态成员为所有类对象所共享,不属于某个具体的实例
b、类静态成员即可用类名::静态成员或者对象.静态成员来访问
c、类静态成员变量必须在类外定义,定义时不添加static关键字
d、类的静态成员函数没有默认的this指针,因此在它里面不能使用任何 非静态成员
e、静态成员和类的普通成员一样,也有public、protected、private3 种访问级别,也可以具有返回值,const修饰符等参数
用静态变量完善一个类创建对象的个数?
a、
class Date
{
public:
Date()
:_count(0)
{
_count++;
}
Date(Date& d)
:_count(d._count++)//用d中的count把当前对象中的count拷贝出来
{
_count++;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
static int _count;//计录创建对象的个数,都为整型
};
int Date::_count = 0;
void TestDate()
{
Date d3;
Date d4(d3);
}
int main()
{
Date d1;
Date d2(d1);
TestDate();
return 0;
}
计数后结果为2
b、在局部范围内不能修改该变量,添加一句"d1._count=20;"编译后会报错,因为_count是类里面的静态成员变量,是类的成员,受访问限定符的限定,是私有的。在类外不能访问一个类的私有成员变量。如下会产生错误:
void TestDate()
{
Date d3;
Date d4(d3);
d1._count = 20;
}
c、把静态成员变量给成公有的,产看对象的地址是否相同
class Date
{
public:
Date()
:_count(0)
{
_count++;
}
Date(Date& d)
:_count(d._count++)//用d中的count把当前对象中的count拷贝出来
{
_count++;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
public:
static int _count;//计录创建对象的个数,都为整型
};
int Date::_count = 0;
void TestDate()
{
Date d3;
Date d4(d3);
}
int main()
{
Date d1;
Date d2(d1);
cout << &d1._count << "==" << &d2._count << endl;
TestDate();
return 0;
}
结果打印的地址相等,说明创建的多个对象只有一个计数变量。
静态成员变量的大小加上和不加最后的成员的个数相同,可以通过在mian()中加入
cout<<sizeof(Date)<<endl;
说明静态成员变量不是每个类中都有一份,在整个类中只保存了一份。所有的类创建出的对象都可以去访问这个静态成员变量,是所有的成员共享的。类对象可以访问,但是不能包含在对象里面,不是对象里面的成员。可以通过在mian()中加入
cout<<Date::_count<<endl;
的方式输出(即加上类的作用域限定符)。
以下两种访问静态成员变量的方式都是正确的
Date d1;
Date d2(d1);
int d;
d=d1._count;
d=Date::_count;
2、静态成员变量给定的是私有的,不能直接从类外知晓类成员创建了多少成员变量。可以在类里面写一个共有的方法,获取;
class Date
{
public:
Date()
:_count(0)
{
_count++;
}
Date(Date& d)
:_count(d._count++)//用d中的count把当前对象中的count拷贝出来
{
_count++;
}
~Date()
{
--_count;
}
int GetCount()
{
return _count;
}
private:
int _year;
int _month;
int _day;
static int _count;//计录创建对象的个数,都为整型
};
int Date::_count = 0;
void TestDate()
{
Date d3;
Date d4(d3);
}
int main()
{
Date d1;
Date d2(d1);
TestDate();
cout << d1.GetCount() << endl;
return 0;
}
问题:其他函数在调用时,会新创建一个对象,使计数结果发生改变,不准确。
void TestDate()
{
Date d1;
Date d2(d1);
Date d3;
Date d4(d3);
cout << d1.GetCount() << endl;
}
void TestFunc()
{
Date d;
d.GetCount();
}
可以给函数加上static的方法,成为静态成员函数,不用通过对象来访问,直接加上类的作用域即可
class Date
{
public:
Date()
:_count(0)
{
_count++;
}
Date(Date& d)
:_count(d._count++)//用d中的count把当前对象中的count拷贝出来
{
_count++;
}
~Date()
{
--_count;
}
static int GetCount()
{
return _count;
}
private:
int _year;
int _month;
int _day;
static int _count;//计录创建对象的个数,都为整型
};
int Date::_count = 0;
void TestDate()
{
Date d1;
Date d2(d1);
Date d3;
Date d4(d3);
cout << d1.GetCount() << endl;
}
void TestFunc()
{
Date d;
d.GetCount();
}
int main()
{
Date d1;
Date d2(d1);
TestDate();
cout << d1.GetCount() << endl;// cout << Date::GetCount() << endl;
return 0;
}
静态成员变量:
1)、静态成员遍量需要放在内外进行重定义,在类里面只能相当于一个声明
2)、静态成员变量是每一个类共享的,不是属于某一个对象,计算大小时只需要考虑非静态成员的大小
3、静态成员函数和普通成员函数的区别:
(1)普通成员函数里面,既可以访问普通成员变量、又可以访问静态成员变量(静态成员变量是所有的类共享的);
(2)在静态成员函数里面,不能访问普通成员变量。(静态成员函数中没有this指针,普通成员函数中有this指针,在调用时必须通过对象进行调用。
(2)调用约定的方式不同:静态成员函数的调用约定是_cdecl,c和c++中函数体默认的调用约定是_cdecl
(3)
不加static时,函数调用几次,构造函数就创建几次
class Test
{
public:
Test()
{
cout << "Test()" << this << endl;
}
};
void Fun()
{
static int a = 12;
a++;
Test t;
}
int main()
{
Fun();
Fun();
Fun();
return 0;
}
加上static后,只调用了一次构造函数。只初始化了一次。
class Test
{
public:
Test()
{
cout << "Test()" << this << endl;
}
};
void Fun()
{
static int a = 12;
a++;
static Test t;
}
int main()
{
Fun();
Fun();
Fun();
return 0;
}
只要一个对象创建好了,后面需要创建的对象可以直接拿来用,不需要进行重新创建。保证静态局部变量只有一份。调用时,只用调一次。
int g_val = 0;
void Fun()
{
static int a = 12;
a++;
static Test t;
if (0 == g_val)
{
Test();
g_val = 1;
}
}
编译期间,就把空间给出来了,但是没有执行构造函数。程序运行时,Test函数在第一次调用时,执行构造函数,把空间构造成一个对象。