【c++系列】面向对象之类和对象

面向对象

补充面向对象概述:参照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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值