一、友元
友元friend机制允许一个类授权其他函数访问它的非公有成员。
友元声明以关键字friend开头 ,它只能出现在类的声明中,它们不受其在类体中的public private和protected区的影响。
友元分为 外部函数友元、成员函数友元和类友元。
友元的特点:
1、不具有对称性:A是B的友元,并不意味着B是A的友元。
2、不具有传递性:A是B的友元,B是C的友元,但是A不是C 的友元。
3、不具有继承性:Base类型继承Object类型,如果Object类型是A的友元,并不意味着Base类型是A的友元。
1.1外部函数友元
class Int
{
private:
int value;
public:
Int(int x = 0) : value(x) {}
~Int() {}
friend Int operator+(const int x, const Int& it);
};
Int operator+(const int x,const Int& it)
{
int val = x + it.value;
return Int(val);
}
int main()
{
Int a(10), b(20);
Int c;
c = 30 + a;
}
此时这个函数可以访问该对象的非公有成员。
1.2成员函数友元
class Test;
class Object
{
private:
int value;
public:
Object(int x) :value(x) {}
~Object() {}
void func(Test it)
{
it.sum += value;
}
};
class Test
{
private:
int sum;
public:
Test(int x = 0) :sum(x) {}
~Test() {}
};
int main()
{
Object obj(10);
Test test(20);
obj.func(test);
}
首先来看这个程序,它有几处错误:
1.这一部分无法编译通过,这样写编译器会认为会有一个test实体对象,无法确定他占多少空间,所以会报错。可以把它变成引用。因为引用本质上是指针,这样系统就知道给它分配多少空间。
void func(Test it)
2.it.sum会报错,虽然这个类已经声明了,但是编译器 并不知道这个类里面有什么,sum对它来说是不可见的,所以会报错。但是我们如果将这个函数放在test类型定义之后,也还是编译不通过,这时候可以识别到test,但是这个函数无法访问test的私有成员。
所以这样才是正确的
class Object
{
private:
int value;
public:
Object(int x) :value(x) {}
~Object() {}
void func(Test& it);
};
class Test
{
private:
int sum;
public:
Test(int x = 0) :sum(x) {}
~Test() {}
friend void Object::func(Test& it);
};
void Object::func(Test& it)
{
it.sum += value;
}
int main()
{
Object obj(10);
Test test(20);
obj.func(test);
}
1.3类友元
这样改就是类友元:
class Test
{
private:
int sum;
public:
Test(int x = 0) :sum(x) {}
~Test() {}
friend class Object;
};
class Object
{
private:
int value;
public:
Object(int x) :value(x) {}
~Object() {}
void func(Test& it);
};
void Object::func(Test& it)
{
it.sum += value;
}
注意:
友元不分公有私有还是保护,它自成一体
二、静态成员
2.1静态数据成员
由关键字static修饰类体中成员,成为类静态成员。类的静态成员为其所有对象共享,不管有 多少对象,静态成员只有一份存于公用内存中。静态数据成员被当作该类类型的全局对象。
全局对象和静态的区别:
在多文件中,用static关键字修饰的变量只能在本文件中使用,而全局对象可以在其他文件中使用。
静态关键字可以修饰局部变量,全局变量,在.data区。当它第一次被调用,就会进行初始化,之后不会在进行初始化。在类中修饰成员函数时,静态是不具有this指针的,而且被所有成员所共享。而且在类中声明,类外定义 。
静态成员必须要在类外进行初始化,不能写在构造 函数中。
因为静态成员只用初始化一次。
在函数体内也不行,因为我们只声明了,并没有定义。
改成这样才可以:
将上述代码中的num定义成公有的可以.访问,直接访问,但是不可以::num访问。
因为num是类域中的而不是全局域里面的。
class Object
{
private:
int value;
public:
static int num;
Object(int x) :value(x) { num += 10; }
~Object() {}
void func() const;
};
int Object::num = 0;
int main()
{
Object obja(10), objb(20), objc(30);
sizeof(obja);//4字节
cout << &obja.num << endl;
cout << ::num << endl;
Object::num = 100;
}
因为const修饰的是this指针,所以value不能进行修改。但是静态成员不具有this指针。
2.2与模板结合
void func(int x)
{
static int num = x;
cout << "&num:" << &num << "num:" << num << endl;
return;
}
int main()
{
func(10); func(20);
}
静态num的值已经确定的,而且只有一个。
但是如果这样:
template<class T>
void func(T x)
{
static int num = x;
cout << "&num:" << &num << "num:" << num << endl;
return;
}
int main()
{
func(10);
func(12.23);
func('a');
}
就会每一个都产生一个num
2.3静态方法
非静态成员函数有this指针,静态成员函数没有this指针。
非静态成员函数既可以访问静态数据又可以访问非静态数据,但是静态成员函数只能访问静态数据,不可以访问非静态数据,原因还是因为没有this指针。
class Object
{
private:
int value;
protected:
static int num;
public:
Object(int x) :value(x) {}
~Object() { }
void Print() const
{
cout << "value:" << value << endl;
cout << "num:" << num << endl;
}
static void func();
};
int Object::num = 0;
void Object:: func()
{
cout << "num:" << num << endl;
}
那么,这个函数可以编译通过吗?
都可以的。
那么静态量与静态常量有什么区别呢?
他们都可以在类内声明,类外定义。 但是,静态常量可以直接在类内给初始值。但是静态量不行。(注意,这里的静态常量必须是整型)
补充:类的设计不可以把自身设计进去(无线递归),但是可以设计静态的。