面向对象
补充面向对象概述:参照c++primer
面向对象三大特性:封装、继承、多态
封装
封装的意义:
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
类和struct的区别:
- 默认的访问权限不同:类默认为私有,struct默认是公有
- class可以定义模板参数 template,而没有template
https://blog.csdn.net/weixin_60630451/article/details/126687902
类和对象
构造函数
有参构造和无参构造(按照参数类型分)
普通构造和拷贝构造(按照使用方式)
#include<iostream>
using namespace std;
class Person {
public:
Person() {
cout << "默认构造函数" << endl;
}
Person(int age1) {
age = age1;
cout << "有参构造" << endl;
}
~Person() {
cout << "析构函数" << endl;
}
public:
int age;
};
int main() {
// 创建对象的方式
cout << "p" << endl;
Person p; // 1. 调用无参构造函数
cout << "p1" << endl;
Person p1(10); // 2.括号法调用有参构造函数
// Person p2(); 注:调用无参构造函数不能加(),否则编译器会认为这是一个函数说明
cout << "p3" << endl;
Person p3 = Person(20); // 3.显式调用有参构造函数
Person p4 = Person(p3);
//Person(10)单独写就是匿名对象 当前行结束之后,马上析构
//2.3 隐式转换法
cout << "p5" << endl;
Person p5 = 10; // Person p5 = Person(10);
Person p6 = p5; // Person p6 = Person(p5);
//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
//Person p5(p4);
}
拷贝构造函数
调用时机:1)用已有的对象初始化另一个对象;2)值传递的方式给函数传参;3)值方式传回局部对象
public Person(const Person & p) {
cout << "拷贝构造函数!" << endl;
age = p.age;
}
void printPserson(Person p) {
cout << "p's name is " << p.age << endl;
};
Person returnPerson(){
Person p = Person(100);
cout << (int *) &p << endl;
return p;
};
int main() {
Person p(10);
// 1.用已有对象初始化另一个对象
Person p1 = Person(p);
Person p2(p);
// 2.值传递的方式给对象传参
printPerson(p);
// 3. 值方式返回局部对象
returnPerson();
}
析构函数:在对象销毁前调用,完成清理工作
深拷贝和浅拷贝:
1)浅拷贝:简单的值拷贝工作
2)深拷贝:在堆区申请一份新的内存空间
默认的拷贝构造函数中是浅拷贝
class Person {
public:
Person() {cout << "默认构造函数" << endl;}
Person(int age, int height) {
cout << "有参构造函数" << endl;
m_age = m_age;
m_height = new int(height);
}
Person(const Person& p) {
m_age = p.m_age;
// 默认拷贝构造
// m_height = p.m_height;
m_height = new int(*p.m_height);
}
~Person() {
// 处理自己申请的内存空间
if (m_height != NULL) {
delete m_height;
m_height = NULL;
}
cout << "析构函数调用~" << endl;
}
int m_age;
int* m_height;
};
int main() {
Person p = Person(10,180);
cout << p.m_age << *p.m_height << endl;
Person p2(p);
cout << p2.m_age << *p2.m_height << endl;
}
初始化列表:
作用:初始化属性的一种方式
语法:构造函数():属性1(值1),属性2(值2)... {}
class Person {
public:
Person(int a, int b, int c): a(10),b(20),c(20) {}
void printPerson() {
cout << "a:" << a << ", b:" << b << ", c:" << c << endl;
}
public:
int a;
int b;
int c;
};
int main() {
Person p;
p.printPerson();//a:10, b:20, c:30
Person p2(30,20,10);
p.printPerson();//a:30, b:20, c:10
}
类对象作为类成员:
构造和析构的顺序:先调用对象成员的构造,再调用本类构造;析构则相反
#include<iostream>
#include<string>
using namespace std;
class Address {
public:
Address(string address) {
m_address = address;
cout << "address类构造方法调用" << endl;
}
~Address() {
cout << "调用addres析构函数" << endl;
}
public:
string m_address;
};
class Person {
public:
// 初始化列表可以告诉编译器调用哪个构造函数
Person(string name, string address):m_name(name),m_address(address) {
cout << "Person 类构造方法调用" << endl;
m_name = name;
m_address = address;
}
~Person() {
cout << "调用person析构函数" << endl;
}
void printPerson() {
cout << m_name << "‘s address is " << m_address.m_address << endl;
}
public:
string m_name;
Address m_address;
};
int main() {
Person p("zzzz","iphone");
p.printPerson();
}
/*
address类构造方法调用
Person 类构造方法调用
address类构造方法调用
调用addres析构函数
zzzz‘s address is iphone
调用person析构函数
调用addres析构函数
*/
静态成员:
静态变量三大特点:
1)所有实例共享一份数据;2)在编译期间分配内存;3)类内声明,类外初始化
静态函数两大特点:
1)所有对象共享一份函数;2)只能访问静态变量
#include<iostream>
using namespace std;
class Person {
public:
static func() {
cout << "func" << endl;
a = 1;
// aa = 11; // 非静态变量无法访问
};
static int a;
int aa;
private: // 静态成员也有访问权限
static func_private() {
cout << "func_private" << endl;
};
static int b;
};
int Person::a = 10; //类内声明,类外初始化
int Person::b = 20;
int main() {
Person p;
// 访问静态变量两种方式
// 1.对象名访问
cout << "a:" << p.a << endl;
// cout << "b:" << p.b << endl; // 私有静态变量外部无法访问
Person p2;
p2.a = 100;
cout << "a:" << p.a << endl;
//2.类名访问
cout << "a:" << Person::a << endl;
// 访问静态函数的两种方式
p.func();
Person::func();
}
c++对象模型和this指针:
成员变量和成员函数分开存储
成员变量和成员函数分开存储,只有非静态成员变量才属于对象
#include<iostream>
using namespace std;
class Person {
public:
// 非静态成员变量,占用对象空间
int a;
// 静态成员变量,不占用对象内存空间,所有对象共享一份内存
static int b;
// 非静态成员函数,不占用对象内存空间
void func1() {
cout << "non static funcion" << endl;
}
// 静态成员函数,也不占用对象内存空间
static void func2() {
cout << "static function" << endl;
}
};
int main() {
Person p;
cout << "sizeof(p)" << sizeof(p) << endl;
cout << "sizeof(Person)" << sizeof(Person) << endl;
}
/*
sizeof(p)4
sizeof(Person)4
*/
空对象占用一个字节,是为了区分对象在内存中的位置
this指针
this指针指向 被调用的函数所属的对象。this指针是隐含在每一个非静态成员函数内部的一种指针。
用途:1)当形参和成员变量重名时,加以区分;2)返回当前对象*this
class Person {
public:
Person(int age) {
this->age = age; // 区分同名变量和形参
}
Person& addAge(Person & p) { // 返回引用
this->age += p.age;
return *this; // 返回对象
}
int age;
}
int main() {
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.addAge(p1).addAge(p1).addAge(p1); // 40
cout << "p2.age = " << p2.age << endl;
}
返回对象:
class Person {
public:
Person(int age) {
this->age = age; // 区分同名变量和形参
}
Person addAge(Person & p) {
this->age += p.age;
return *this; // 返回对象
}
int age;
}
int main() {
Person p1(10);
cout << "p1.age = " << p1.age << endl;
Person p2(10);
p2.addAge(p1).addAge(p1).addAge(p1); // 20
cout << "p2.age = " << p2.age << endl;
}
空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断保证代码的健壮性
//空指针访问成员函数
class Person {
public:
void ShowClassName() {
cout << "我是Person类!" << endl;
}
void ShowPerson() {
if (this == NULL) {
return;
}
cout << mAge << endl;
}
public:
int mAge;
};
void test01()
{
Person * p = NULL;
p->ShowClassName(); //空指针,可以调用成员函数
p->ShowPerson(); //但是如果成员函数中用到了this指针,就不可以了
}
int main() {
test01();
system("pause");
return 0;
}
const修饰成员函数
const修饰的成员函数为常函数,常函数内不能修改属性,除非该属性被muteble修饰。
const修饰的对象为常对象,常对象只能调用常函数(因为非 常函数可以修改属性)
class Person {
public:
Person() {
a = 10;
b = 10;
}
// this指针的本质是指针常量,指针的指向不可以修改 Person * const this
// 如果需要指针指向的值也不可以修改,则需要使用常函数,这样相当于const Person* const this
void showPerson() const {
// this->a = 100; 不可以修改
this->b = 100;
}
void func() const {}
int a;
mutable int b;
}
int main() {
Person const p;
cout << p.a << endl; // 常对象可以访问变量的值
// p.a = 1000; 但不能改变变量的值
p.b = 1000; // 但可以改变mutable修饰的变量值
p.func(); // 常对象只能调用常函数
}
友元:
允许在类外访问私有变量。
友元的三种形式:1)全局函数做友元;2)成员函数做友元;3)类做友元
友元函数:
class House {
// 友元可以在类的任何地方声明,不受访问控制影响
friend void goodFriend(House * house);
public:
House() {
this->sittingroom="客厅";
this->bedroom="卧室";
}
string sittingroom;
private:
string bedroom;
}
void goodFriend(House* house) {
cout << "好朋友访问客厅" << endl;
cout << "好朋友访问卧室" << endl;
}
int main() {
House house;
goodFriend(&house);
}
友元类:
class Building;
class goodGay
{
public:
goodGay();
void visit();
private:
Building *building;
};
class Building
{
//告诉编译器 goodGay类是Building类的好朋友,可以访问到Building类中私有内容
friend class goodGay;
public:
Building();
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom;//卧室
};
Building::Building()
{
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
goodGay::goodGay()
{
building = new Building;
}
void goodGay::visit()
{
cout << "好基友正在访问" << building->m_SittingRoom << endl;
cout << "好基友正在访问" << building->m_BedRoom << endl;
}
void test01()
{
goodGay gg;
gg.visit();
}
int main(){
test01();
system("pause");
return 0;
}
友元成员函数:
class Building;
class goodGay
{
public:
goodGay();
void visit(); //只让visit函数作为Building的好朋友,可以发访问Building中私有内容
void visit2();
private:
Building *building;
};
class Building
{
//告诉编译器 goodGay类中的visit成员函数 是Building好朋友,可以访问私有内容
friend void goodGay::visit();
public:
Building();
public:
string m_SittingRoom; //客厅
private:
string m_BedRoom;//卧室
};
Building::Building()
{
this->m_SittingRoom = "客厅";
this->m_BedRoom = "卧室";
}
goodGay::goodGay()
{
building = new Building;
}
void goodGay::visit()
{
cout << "好基友正在访问" << building->m_SittingRoom << endl;
cout << "好基友正在访问" << building->m_BedRoom << endl;
}
void goodGay::visit2()
{
cout << "好基友正在访问" << building->m_SittingRoom << endl;
//cout << "好基友正在访问" << building->m_BedRoom << endl;
}
void test01()
{
goodGay gg;
gg.visit();
}
int main(){
test01();
system("pause");
return 0;
}
运算符重载:
加号运算符:
类似于函数重载,对运算符赋予不同的功能和作用范围
两种方式:1)成员函数实现;2)全局函数方式实现
两种不同的实现方式 在函数调用时方式也不同
#include<iostream>
using namespace std;
class Person {
public:
Person(){}
Person(int m_a, int m_b) {
this->m_a = m_a;
this->m_b = m_b;
}
// 成员函数 重载
Person operator+(const Person& p) {
Person tmp;
tmp.m_a = this->m_a + p.m_a;
tmp.m_b = this->m_b + p.m_b;
return tmp;
}
int m_a;
int m_b;
};
// 全局函数-重载
Person operator+(const Person & p1, const Person & p2) {
Person p;
p.m_a = p1.m_a+p2.m_a;
p.m_b = p2.m_b+p2.m_b;
return p;
};
// 运算符重载+函数重载
Person operator+(const Person & p1, int m) {
Person p;
p.m_a = p1.m_a + m;
p.m_b = p1.m_b + m;
return p;
};
int main() {
Person p1(10, 10);
Person p2(20, 20);
Person p3 = p1 + p2; // p3 = p1.operator+(p2)
cout << "p3.m_a:" << p3.m_a << endl;
cout << "p3.m_b:" << p3.m_b << endl;
Person p4 = p3 + 100; // p4 = operator+(p3, 100)
cout << "p4.m_a:" << p4.m_a << endl;
cout << "p4.m_b:" << p4.m_b << endl;
}
左移运算符:
实现cout<<p
的功能。但只能用全局函数实现,使用成员函数方式在调用时是这样的p<<cout
,这不符合使用习惯。(有点类似java的tostring方法)
#include<iostream>
using namespace std;
class Time {
public:
friend ostream& operator<<(ostream& out, const Time& time); // 友元可以在类的任何地方声明
Time(){};
Time(int hour, int minute) {
this->hour = hour;
this->minute = minute;
}
/* 成员函数方式重载<<,调用方式 time<<out
void operator<<(ostream &out) {
...
}*/
private:
int minute;
int hour;
};
ostream& operator<<(ostream& out, const Time& time) {
out << "hour: " << time.hour << ", minute: " << time.minute << endl;
return out;
};
int main() {
Time time(14, 20);
cout << time << "hello world" << endl; // 返回引用实现链式编程
}
递增运算符:
前++、后++实现不同
class MyInteger {
friend ostream& operator<<(ostream& out,const MyInteger& myinteger);
public:
MyInteger(){ m = 0;}
MyInteger(int n) { this->m = n;};
// 前++
MyInteger& operator++() {
this->m += 1;
return *this;
};
// 后++
MyInteger operator++(int) {
MyInteger tmp = *this;
// tmp.m = this->m;
this->m += 1;
return tmp;
}
private:
int m;
};
ostream& operator<<(ostream& out,const MyInteger& myinteger){
out << "integer is:" << myinteger.m << endl;
return out;
};
int main() {
// Time time(14, 20);
// cout << time << "hello world" << endl; // 返回引用实现链式编程
MyInteger myinteger(100);
cout << myinteger++ << endl;
cout << myinteger << endl;
cout << ++myinteger << endl;
}
赋值运算符重载:
c++编译器给类添加4个默认函数:1)构造函数;2)析构函数;3)拷贝构造函数;4)赋值运算符重载函数
赋值运算符重载提供的是浅拷贝,当类中存在堆内存数据时,会出现深浅拷贝问题
class Person {
public:
Person() { this->age = new int(20); };
Person(int age) {this->age = new int(age); };
~Person() {
if (age != NULL) {
delete age;
this->age = NULL;
}
};
// 重载=运算符
Person& operator=(const Person& p) {
if (age != NULL) {
delete age;
age = NULL;
}
// this->age = p.age; // 编译器提供默认=重载
age = new int(*p.age);
return *this;
};
public:
int *age;;
};
int main() {
// Time time(14, 20);
// cout << time << "hello world" << endl; // 返回引用实现链式编程
//MyInteger myinteger(100);
//cout << myinteger++ << endl;
//cout << myinteger << endl;
//cout << ++myinteger << endl;
Person p1(10);
Person p2(20);
Person p3(30);
p1 = p2 = p3;
cout << *p1.age << endl;
cout << *p2.age << endl;
cout << *p3.age << endl;
}
关系运算符重载:
bool operator==(const Person& p) {
if (*p.age == *(this->age)) {
return true;
} else {
return false;
}
};
函数调用运算符重载:
- 函数调用运算符 () 也可以重载
- 由于重载后对象的方法使用 非常像函数的调用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
class MyAdd
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
void test02()
{
MyAdd add;
int ret = add(10, 10);
cout << "ret = " << ret << endl;
//匿名对象调用
cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}