友元:
友元函数:friend
friend函数可以访问私有成员。
#include <iostream> using namespace std; class Point { private: int x; int y; public: Point(int a = 0, int b = 0) : x(a), y(b) {} // 友元函数声明:friend 写在最前面 friend Point operator+(const Point &p1, const Point &p2); friend ostream& operator<<(ostream &os, const Point &p); }; // 友元函数定义,类外,不加friend Point operator+(const Point &p1, const Point &p2) { Point res; res.x = p1.x + p2.x; res.y = p1.y + p2.y; return res; } ostream& operator<<(ostream &os, const Point &p) { os << "(" << p.x << "," << p.y << ")"; return os; } int main() { Point p1(1,2); Point p2(3,4); Point p3 = p1 + p2; cout << p3 << endl; return 0; }一个问题:
我们在编辑上述两个代码时,第二个函数在调用第一个函数的时候,由于this指针具有常性,直接调用会导致this指针权力的膨胀。如何解决这个问题?
我们可以通过这种方法:
来限制this指针的权力,同时,const修饰的是*this,本质改变this的类型。(这是硬性规定,不是逻辑问题)
需要注意的是,在使用const修饰符来限制this指针的时候,该函数中赋值this指针的函数将不能正常使用。
再谈构造函数(初始化列表):
之前我们提过默认构造的两种条件:
1.完全没有参数:
class Myqueue { public: //无参数 Myqueue() { } }; Myqueue s;//可以正常使用2.有参数,但全部参数都给了默认值:
class Myqueue { public: //有参,但有默认值 Myqueue(int cap = 10) { } }; Myqueue s;//可以正常使用但是当我们在写列表的时候,有时会面临给参数但无法构成默认构造的情况。比如:
class Myqueue { public: //有参,无默认值 Myqueue(int cap) { } }; Myqueue s;//不可以正常使用因为Myqueue有参数,但是参数无默认值,所以没有默认构造。
我们也可以通过直接传值进行构造:
class Myqueue { public: Myqueue(int cap) { } }; int main(){ Myqueue q(10); return 0; }这样可以通过传参进行构造,即初始化(不是默认构造)。
但是,当我们通过栈实现队列的时候:
typedef int DataType; class Stack { public: Stack(size_t capacity) { _array = (DataType*)malloc(sizeof(DataType) * capacity); if (NULL == _array) { perror("malloc申请空间失败!!!"); return; } _capacity = capacity; _size = 0; } private: DataType* _array; size_t _capacity; size_t _size; }; class Myqueue { public: Myqueue(int cap) { } private: Stack _pushst; Stack _popst; int _size; }; int main(){ Myqueue q(10); return 0; }此时我们仍旧可以通过传参来解决Myqueue的构造问题,但是Myqueue中含有Stack自定义类型,我们在调用Myqueue的时候也会调用Stack,此时我们无法在给Myqueue传参的同时直接给Stack传参。但是我们可以直接初始化列表:
class Myqueue { public: Myqueue(int n = 0) //初始化列表 :_pushst(n) ,_popst(n) ,_size(0) { } private: Stack _pushst; Stack _popst; int _size; }; int main(){ Myqueue q(10); return 0; }冒号开始,逗号分割。
初始化列表可以理解为每个对象中成员定义的地方。
class Myqueue { public: Myqueue(int n = 0) //初始化列表 :_pushst(n) ,_popst(n) ,_size(0) ,_x(0) { //这里是函数体内部 } private: Stack _pushst; Stack _popst; int _size; }; int main(){ Myqueue q(10); return 0; }——const类型、引用类型以及没有默认构造的自定义类型的成员必须在初始化列表初始化(必须显式传参构造)而不能在函数体内初始化。
如果我们把Myqueue 中的成员初始化取消,只在函数体内部声明会怎样?
class Myqueue { public: Myqueue(int n = 0) //初始化列表 { //函数体内部 } private: Stack _pushst; Stack _popst; int _size; }; int main(){ Myqueue q(10); return 0; }会发现初始化列表中的成员都会先走一遍初始化列表,自定义类型会调用默认构造。
实践中尽可能使用初始化列表,不再方便使用函数体初始化。
-成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
因为在这个类中a2声明比a1早,所以在初始化列表中,a2比a1先初始化。
所以优先将没有初始化的a1赋值给a2致使a2为随机值,而又将a赋值给a1。
最后的输出结果为:1和随机值。
所以实战中尽量保持初始化列表与成员变量声明顺序一致。
隐式类型转换:
const A& raa = 3;
在这行代码中,不存在拷贝构造,因为3在传给A&之前进行临时拷贝,A&直接作为该临时拷贝的别名。
explicit函数:
该函数可以防止类进行类型转换。
static: