2019.01.14
1、普通成员变量 ===> 结构体的成员
2、静态的成员变量 ===> 全局变量
3、类的普通成员函数 ===> 会为其添加一个指向当前对象的指针
4、类的静态成员函数 ===> 没有为其添加指针,原来的样子
// ===> 没有指向当前对象的指针,所以不能使用对象内部的成员
// ===> 可以使用静态成员变量,因为静态变量实现为全局变量,可见所以可用
全局函数想使用类的成员变量(属性),只能通过对象来使用。
友元
友元:可以访问类的私有成员
友元函数:
如果一个函数是类A的友元函数,则该函数可以使用(通过对象)A的所有成员。
友元函数声明方式:在类中写上函数声明,在函数声明前加上friend(关键字)。
使用注意:
- 友元声明不受访问控制符限制(private、protected、public),可放在类的任意位置(一般放在类的最开始)。
2.友函数是“友”,不是类的成员函数,是外部函数,没有this指针。
3.友元破坏了类的封装性,------->慎使用(尽量不要用)。
例子:
class Test
{
friend void show(const Test &t); // show 是 Test 的友元函数
public:
Test(int a, int b)
{
this->m_a = a;
this->m_b = b;
}
private:
int m_a;
int m_b;
};
void show(const Test &t)
{
printf ("a = %d, b = %d\n", t.m_a, t.m_b);
}
友元类:
若类A是类B的友元类,则类A中所有函数都是类B的友元函数。
例子:
class Address
{
friend class Student;
public:
Address(const char *shen, const char *city, const char *town, const char *street)
{
this->shen = shen;
this->city = city;
this->town = town;
this->street = street;
}
private:
const char *shen;
const char *city;
const char *town;
const char *street;
};
// 友元类:如果类A是类B的友元类,则类A中所有函数都是类B的友元函数
class Student
{
public:
Student(int id, const char *name, const char *shen, const char *city, const char *town, const char *street);
void show()
{
printf ("id = %d, name = %s\n", id, name);
printf ("addr = %s %s %s %s\n", addr.shen, addr.city, addr.town, addr.street);
}
private:
int id;
const char *name;
Address addr;
};
运算符重载
// 运算符重载:
// 1、外部实现
// 2、内不实现:外部到内部 ----> 去掉左操作数,由this指针代替
// 注意:
// 1、不能改变运算符的优先级
// 2、不能改变运算符的操作数个数
// 3、不能自创运算符
// 4、同一种运算,内部和外部实现只能存在一个
例子:
用类实现复数
class Complex
{
friend Complex operator+(Complex &c1, Complex &c2);
friend Complex operator+(Complex &c1, int num);
public:
Complex(int a = 0, int b = 0)
{
m_a = a;
m_b = b;
}
void show()
{
if (m_b == 0)
cout << m_a << endl;
else if(m_b>0)
cout << m_a << " + " << m_b << "i" << endl;
else
cout << m_a << " - " << m_b*-1 << "i" << endl;
}
Complex operator-(Complex &c2)
{
Complex tmp(m_a - c2.m_a, m_b - c2.m_b);
return tmp;
}
Complex operator -(int num)
{
Complex tmp(m_a - num, m_b);
return tmp;
}
private:
int m_a; // 实部
int m_b; // 虚部
};
// Complex 是自定义类型,编译不知道运算规则
//1、判断运算方式: ===> 做加法运算
//2、调用相关函数进行运算: ====>
// 函数:
// 1、函数名 : operator + 运算符,例如:做加法运算 operator+ 做减法运算: operator-
// 2、函数参数 :参与运算的操作数,从左到右写 : operator+(c1, c2)
// 3、函数的返回值: 根据需要确定 ===> Complex operator+(Complex &c1, Complex &c2)
// 函数找到 —> 直接调用 找不到:出错 ----> 自己写
左移和右移只能在类外部重载。
左操作数不能改变的运算符都不能在类内重载。
运算符尽量在类内重载。当左操作数不能改变时只能在类外重载(用全局函数)。
不能用友元函数重载的(只能在类内重载):
= () [] ->
自增和自减
例子:
在类内部实现 前置-- 和 后置–
// 前置--
Complex &operator --()
{
m_a--;
m_b--;
return *this;
}
// 后置--
Complex operator --(int)
{
Complex tmp(c.m_a, c.m_b);
m_a--;
m_b--;
return tmp;
}
在类外部实现 前置++ 和 后置++
// 前置++
Complex &operator ++(Complex &c)
{
c.m_a++;
c.m_b++;
return c;
}
// 后置++: 多了一个 int 的占位参数,和前置++进行区分
Complex operator ++(Complex &c, int)
{
Complex tmp(c.m_a, c.m_b);
c.m_a++;
c.m_b++;
return tmp;
}
通过上述4个例子发现,后置比前置多拷贝了一次,所以前置比后置效率高。
左移<< 右移>> 的重载
例子:
//右移的重载
ostream &operator<<(ostream &out, const Complex &c)
{
if(c.m_b == 0)
{
out << c.m_a << endl;
}
else if(c.m_b > 0)
{
out << c.m_a << "+" << c.m_b << "i" << endl;
}
else
{
out << c.m_a << "-" << c.m_b * (-1) << "i" << endl;
}
return out;
}
//左移的重载
istream &operator >>(istream &in, const Complex &c)
{
in >> c.m_a >> c.m_b;
return in;
}
赋值符 = 的重载
例子:用类实现数组
class MyArray
{
public:
MyArray(int a);
int length();
MyArray(const MyArray &obj);
void setData(int index, int data);
int getData(int index);
~MyArray();
int &operator[](int index);
MyArray &operator =(const MyArray &obj);
private:
int *m_a;
int m_len;
};
MyArray &MyArray::operator =(const MyArray &obj)
{
if(this != &obj)//判断是否是自己给自己赋值
{
//1.创建一个临时变量 == 拷贝构造函数
MyArray tmp = obj;//会调用拷贝构造函数
//2.当前对象和tmp交换指针空间
int *p = tmp.m_a;
tmp.m_a = m_a;
m_a = p;
//3.复制其他变量
m_len = tmp.m_len;
}
return *this;
}
数组下标 [ ] 的重载
int &MyArray::operator[](int index)
{
return m_a[index];
}