运算符重载(左移运算符)

左移运算符重载

  • 通常不会利用成员函数重载左移运算符。因为如果按照 类名 operator <<(ostream &cout) ,那么实例化一个对象p,调用时简化版本为 p << cout ;,这和想要实现的就相反了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
using namespace std;
class Person {
public:
Person(int a, int b) :m_a(a), m_b(b) { }
Person& operator << (ostream& out) {
cout << "m_a = " << this->m_a << " m_b = " << this->m_b << endl;
return *this;
}
private:
int m_a;
int m_b;
};
void test01() {
Person p(10,10);
p << cout <<cout;
}
int main() {
test01();
return 0;
}
  • 可以利用全局函数重载左移运算符。

    注意:

    (1)cout 做形参时只能是引用。c++中std::ostream这个类只有一个实例 cout ,按值传递相当于在重载函数 中又定义了一个 ostream 实例,只是不允许的,但是使用引用传递的话便还是原来的 cout,只是作为一个别名存在。

    (2)为了实现链式输出,返回值需要为 cout ,所以函数返回值类型为 ostream &

    (3)注意形参可以写成其他的名字,比如 ostream &out,因为 out 相当于是 cout 的别名。

    (4)可以配合友元实现输出自定义数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;
class Person {
//声明此函数为友元函数,可以防私有属性
friend ostream& operator << (ostream& out, Person& p);
public:
Person(int a, int b) :m_a(a), m_b(b) { }
private:
int m_a;
int m_b;
};
ostream& operator << (ostream &out ,Person &p) {
cout << "m_a = " << p.m_a << " m_b = " << p.m_b << endl;
return out;//实现链式输出
}
void test01() {
Person p(10,10);
cout << p << endl;
}
int main() {
test01();
return 0;
}

运算符重载(加号)

加号运算符重载

  • 作用:实现两个自定义数据类型相加的运算

  • 方法: 返回值 operator +(形参列表)

    (1)通过成员函数重载 + 号

    (2)全局函数重载 + 号

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<iostream>
using namespace std;
class Person {
public:
int m_a;
int m_b;
Person() {
}
Person(int a, int b):m_a(a),m_b(b){}
//通过成员函数重载 + 号
Person operator + (Person &p1) {
Person temp;
temp.m_a = this->m_a + p1.m_a;
temp.m_b = this->m_b + p1.m_b;
return temp;
}
};
//全局函数重载
//Person operator + (Person& p1, Person& p2) {
// Person temp;
// temp.m_a = p1.m_a + p1.m_a;
// temp.m_b = p2.m_b + p1.m_b;
// return temp;
//}
void test01() {
Person p1(10,10);
Person p2(10,10);
Person p3 = p1 + p2;
//成员函数调用本质:Person p3 = p1.operator+(p2);
cout << p3.m_a << " " << p3.m_b << endl;
Person p4 = p1 + p2;
//全局函数本质调用:Person p4 = operator+(p1,p2);
cout << p3.m_a << " " << p3.m_b << endl;
}
int main() {
test01();
return 0;
}
  • 运算符重载也可以实现函数重载
1
2
3
4
Person operator + (Person& p1, Person& p2) {
}
Person operator + (Person& p1, int a){
}

友元

友元

  • 在程序中,当类外特殊的一些函数或者类想要访问某些私有属性时,可以用到友元的技术,友元的目的就是让一个函数或者类访问另一个类中的私有成员

  • 友元的关键字为 friend

  • 友元的三种实现

    (1)全局函数做友元

    (2)类做友元

    (3)成员函数做友元

1. 全局函数做友元

  • 将全局函数在类中进行声明,前面加上 friend 关键字

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
using namespace std;
class Building {
//GoodFriend是全局函数,声明为Building的友元,可以访问类中的私有成员
friend void GoodFriend(Building& building);
public:
string m_sittingRoom;
Building() {
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
private:
string m_bedRoom;
};
void GoodFriend(Building& building) {
cout << "友元函数访问公有成员m_sittingRoom:" << building.m_sittingRoom << endl;
cout << "友元函数访问私有成员m_bedRoom:" << building.m_bedRoom << endl;
}
void test01() {
Building building;
GoodFriend(building);
}
int main() {
test01();
return 0;
}

2. 类做友元

  • 语法:声明 B 是 A 的友元类,则 B 类对象成员可以访问 A 的私有属性
1
2
3
4
5
6
class A{
friend class B;
};
class B{
A a;
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include<iostream>
using namespace std;
//此处不需要提前声明 GoodFriend,因为在building中声明友元时已经指出是类,编译器也会知道GoodFriend是类
//GoodFriend类要使用Building类,则必须先定义或者声明Building类
class Building {
friend class GoodFriend;
//表明GoodFriend类是Building的友元类,GoodFriend类可以访问Building的私有成员
public:
string m_sittingRoom;
Building();
private:
string m_bedRoom;
};
class GoodFriend
{
public:
GoodFriend();//构造函数
void visit();
Building * buil;
};
//类外实现成员函数
Building::Building() {
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
GoodFriend::GoodFriend() {
buil = new Building;
}
void GoodFriend::visit() {
cout << "友元函数访问私有成员m_sittingRoom:" << buil->m_sittingRoom << endl;
cout << "友元函数访问私有成员m_bedRoom:" << buil->m_bedRoom << endl;
}
void test01() {
GoodFriend myfriend;
myfriend.visit();
}
int main() {
test01();
return 0;
}

3. 成员函数做友元

  • 先定义的类的成员函数作为后定义的类的友元函数,或者提前声明好避免出错

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<iostream>
using namespace std;
class Building;//提前声明要用到的类
class GoodFriend
{
public:
GoodFriend();//构造函数
void visit();
Building* building;
};
class Building {
friend void GoodFriend::visit();
public:
string m_sittingRoom;
Building();
private:
string m_bedRoom;
};

//类外实现成员函数
Building::Building() {
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
GoodFriend::GoodFriend() {
building = new Building;
}
void GoodFriend::visit() {
cout << "友元函数访问私有成员m_sittingRoom:" << building->m_sittingRoom << endl;
cout << "友元函数访问私有成员m_bedRoom:" << building->m_bedRoom << endl;
}
void test01() {
GoodFriend myfriend;
myfriend.visit();
}
int main() {
test01();
return 0;
}

const修饰成员函数

const修饰成员函数

  • this指针的本质是一个指针常量,即this指针的指向不可以修改,但是指向对象内容可以修改。

  • 常函数

    (1)成员函数加 const 后为常函数,此时this指针实质上理解为const 类名 *const this。所以在常函数内不可以修改成员属性。

    (2)成员属性声明时加关键字 mutable 后,在常函数中依然可以被修改。

  • 常对象

    (1)声明对象前加 const 为常对象

    (2)常对象只能调用常函数

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<iostream>
using namespace std;
class Person {
public:
int m_Age;
mutable int m_B;//在常函数中也可以被修改
//Person *const this
void showClassName() {
this->m_Age = 10;
//this = NULL;报错
cout << "this is Person class" << endl;
}
//const Person *const this
void showPerson()const {
this->m_B = 10;
//this->m_Age = 10;//不可修改
//this = NULL;
cout << "age = " << m_Age << endl;
}
void func() {
}
};
void test01() {
//常对象只能调用常函数
const Person p;
//p.m_Age = 10;//报错
p.m_B = 10;
p.showPerson();
//p.func();//报错,常对象不可以调用普通成员函数,
}
void test02() {
Person p;
p.showPerson();//this指针指向p
}
int main() {
test01();
test02();
return 0;
}

空指针访问成员函数

空指针访问成员函数

  • c++中空指针可以调用成员函数,但是要注意有没有用到this指针
  • 如果要用到this指针,需要加以判断保证代码的健壮性

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<iostream>
using namespace std;
class Person {
public:
int m_Age;
void showClassName() {
cout << "this is Person class" << endl;
}

void showPersonAge1() {
if (this == NULL) {//空指针判断
return;
}
cout << "age = " << m_Age << endl;
}
void showPersonAge() {
cout << "age = " << m_Age << endl;
//等价于 cout << "age = " << this.m_Age << endl;
}
};
void test02() {
Person* p;
p->showClassName();
//报错原因是传入的指针是NULL
//p->showPersonAge();
p->showPersonAge1();
}
int main() {
test02();
return 0;
}

this指针

this 指针

  • 每个非静态成员函数只会诞生一份函数实例,即多个同类型的对象会共用一块代码

  • c++通过提供 this 指针指向被调用的成员函数所属的对象。

  • this 指针是隐含每一个非静态成员函数内的一种指针

  • 用途:

    (1)当形参和成员变量同名时,可用this指针来区分

    (2)在类的非静态成员函数中返回对象本省,可使用 return *this

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
using namespace std;
class Person {
public:
int age;
Person(int age) {
this->age = age;//解决命名冲突
}
Person& PersonAddAge(Person& p) {
this->age += p.age;
return *this;
}
};
void test01() {
Person p1(10);
cout << "p1 的年龄:" << p1.age << endl;
}
void test02() {
Person p1(10);
Person p2(10);
//链式编程思想
p2.PersonAddAge(p1).PersonAddAge(p1);
cout << "p2 的年龄为:" << p2.age << endl;
}
int main() {
test01();
test02();
return 0;
}

运行结果:

1
2
p1 的年龄:10
p2 的年龄:30
  • 上述代码部分修改为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;
class Person {
public:
int age;
Person(int age) {
this->age = age;//解决命名冲突
}
Person PersonAddAge(Person& p) {
this->age += p.age;
return *this;
}
};
void test02() {
Person p1(10);
Person p2(10);
cout << "p2.age:" << p2.age << endl;
}
int main() {
test02();
return 0;
}

运行结果为:

1
p2.age:10

注解:以值方式返回时,会重新拷贝一个对象,这个对象并不指向调用这个函数的对象自身。

成员变量和成员函数分开存储

成员变量和成员函数分开存储

  • 在c++中,类内的成员变量和成员函数分开存储
  • 只有非静态成员变量才属于类的对象上

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
using namespace std;
class PersonNull {
};
class Person {
int m_a; //非静态成员变量,属于类对象上
static int m_b;//静态成员变量 不属于类对象上
void func() {}//非静态成员函数 不属于类对象上
};
int Person::m_b = 0;
void test01() {
PersonNull p;
//空对象占用内存看空间
//c++ 会给每个空对象也分配一个字节空间,每个空对象也应该有一个独一无二的内存地址
cout << "size of p = " << sizeof(p) << endl;
}
void test02() {
Person p;//非空对象
//非静态成员变量属于类对象
cout << "size of p = " << sizeof(p) << endl;
}
int main() {
test01();
test02();
return 0;
}

运行结果:

1
2
size of PersonNull p = 1
size of Person p = 4

静态成员

静态成员

  • 静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。

  • 静态成员变量

    (1)所有对象共享一段内存

    (2)在编译阶段分配内存

    (3)类内声明,类外初始化

    示例:

    1
    2
    3
    4
    5
    class A{
    public:
    static int a;
    }
    int A::a = 100;

    ​ (4)访问方式:通过类名或者对象进行访问,私有静态成员类外不可访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #include<iostream>
    using namespace std;
    class Person {
    public:
    static int m_A;
    private:
    static int m_B;//静态成员变量访问权限,类外不可访问
    };
    //类外初始化
    int Person::m_A = 100;
    int Person::m_B = 100;
    void test01() {
    Person p;
    cout << p.m_A << endl;
    Person p1;
    p1.m_A = 200;
    cout << p.m_A << endl;
    }
    //访问方式
    void test01() {
    //1、通过对象进行访问
    Person p;
    cout << p.m_A << endl;
    //2、通过类名进行访问
    cout << Person::m_A << endl;
    //cout << Person::m_B << endl;//类外不可访问私有静态变量
    }
    int main() {
    test01();
    return 0;
    }
  • 静态成员函数

    (1)所有对象共享同一个函数

    (2)静态成员函数只能访问静态成员变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include<iostream>
    using namespace std;
    class Person {
    public:
    static int m_A;
    //静态成员变量访问权限
    static void func() {
    m_A = 50;
    //m_B = 100;//报错,静态成员函数不可以访问非静态成员变量
    cout << "static void func()的调用。" << endl;
    }
    private:
    int m_B;
    static void func1() {
    cout << "static void func1()的调用。" << endl;
    }
    };
    //类外初始化
    int Person::m_A = 100;

    //访问方式
    void test01() {
    //1、通过对象进行访问
    Person p;
    p.func();
    //p.func1();//不可再类外访问私有静态成员函数
    //2、通过类名进行访问
    Person::m_A;
    }
    int main() {
    test01();
    return 0;
    }

对象成员

类对象作为类成员

  • c++类中的成员可以是另一个类的对象,即对象成员。

例如:

1
2
3
4
5
class A{}
class B
{
A a;
}
  • 当其他类的对象作为本类成员,先构造类成员对象,在构造自身,析构的顺序和构造相反。
  • 要提前声明或者定义要使用要的类。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<iostream>
using namespace std;
class Phone {
public:
string m_pName;
string m_pNumber;
Phone(string pName,string pNumber):m_pName(pName),m_pNumber(pNumber) {
cout << "Phone的构造函数调用。" << endl;
}
~Phone() {
cout << "Phone的析构函数调用。" << endl;
}
};
class Person {
public:
string m_name;
Phone m_phone;
//Phone m_phone(pName,pNumber)
Person(string name, string pName, string pNumber) :m_name(name), m_phone(pName, pNumber) {
cout << "Person的构造函数调用。" << endl;
}
~Person() {
cout << "Person的析构函数调用。" << endl;
}
};
void test01() {
Person p("张三","iPhone 7","123456");
cout << "姓名:" << p.m_name << " 手机型号:" << p.m_phone.m_pName << "手机号码:" << p.m_phone.m_pNumber << endl;
}
int main() {
test01();
return 0;
}

运行结果:

1
2
3
4
5
Phone的构造函数调用。
Person的构造函数调用。
姓名:张三 手机型号:iPhone 7手机号码:123456
Person的析构函数调用。
Phone的析构函数调用。

初始化列表

初始化列表

  • 作用:c++提供了初始化列表语法,用来初始化属性
  • 语法:构造函数(值1,值2):属性1(值1),属性2(值2)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
class Person {
public:
//传统方式初始化
Person(int a, int b):m_a(a),m_b(b) {

}
void PrintPerson() {
cout << "A = " << m_a << " B = " << m_b << endl;
}
private:
int m_a;
int m_b;
};
int main() {
Person p(10, 20);
p.PrintPerson();
return 0;
}
  • 传统初始化函数
1
2
3
4
Person(int a, int b) {
m_a = a;
m_b = b;
}