C++面向对象---NO1.封装(类&对象)

1.类&对象

//Person类
class Person
{
	public:
		Person() {}; //默认构造
		Person(int a,int b) {}; //有参构造
		Person(const Person& p) {}; //拷贝构造
		
		//成员函数
		void init(int a,int b)
		{
			this->m_a = a;
			this->m_b = b;
		}
		int get_max()
		{
			return max(this->m_a,this->m_b); 
		}
	private:
		int m_a; //成员变量
		int m_b; //成员变量
}

Person p; //无参构造,对象p
Person p1(10,20); //有参构造,对象p1
Person p2(P1); //拷贝构造,对象p2

2.类访问修饰符

类成员被访问限定符限定访问范围,有三种访问修饰符,public公有权限,protected保护权限,private私有权限,类的默认访问权限是private权限

class Person
{
	//公有权限
	public:
		int m_a; //类内类外都可以访问
	//保护权限
	protected:
		int m_b; //类外不能访问,派生类可以访问,类内和友元可以访问
	//私有权限
	private:
		int m_c; //类外不能访问,派生类不能访问,类内和友元可以访问
}

protected权限和private权限,类外都不可以访问,两者的区别就在继承的时候出现。

3.构造函数&析构函数

3.1构造函数

  • 类的构造函数是一种特殊的成员函数,在创建类的对象时就会自动调用。构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
  • 构造函数有三种,分别是默认构造,有参构造,拷贝构造。

构造函数的调用规则:默认情况下,编译器会给一个类提供四个成员函数,分别是默认构造,拷贝构造,(默认析构,重载operator= )只考虑构造函数 。如果用户自己定义了默认构造或者有参构造,那么编译器只会提供拷贝构造;如果用户自己定义了拷贝构造,那么编译器不会再提供构造函数

class Person
{
	public:
		Person() {cout << "Person的无参构造!" << endl;} //无参构造
		Person(int a,int b) : m_a(a),m_b(b) //初始化列表
		{cout << "Person的有参构造!" << endl;} //有参构造
		Person(const Person& p) {this->m_a = p.m_a; this->m_b = p.m_b;} //拷贝构造(浅拷贝)
	private:
		int m_a;
		int m_b;
}

3.2析构函数

  • 类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
  • 析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
  • 析构函数最主要的一个作用就是,对象中开辟在堆区的成员,在对象被销毁时能够将堆区的数据也销毁(堆区需要手动释放数据)
class Person
{
	public:
	Person()
	{
		this->p = new int(10); //在堆区开辟一段内存4字节,数据是10
	}
	~Person()
	{
		cout << "Person析构函数调用!" << endl;
		//释放堆区数据
		if(p)
			delete p;
		p = nullptr;
	}
	private:
		int m_a;
		int* p; //将这个成员指针指向堆区数据
}

4.拷贝构造函数

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
拷贝构造函数通常用于:

  • 通过使用另一个同类型的对象来初始化新创建的对象。
  • 复制对象把它作为参数传递给函数。
  • 复制对象,并从函数返回这个对象。
    (后面在深入理解对象模型中会再解释)
    当用户没有自己定义拷贝构造时,编译器会提供一个完全赋值的构造函数(浅拷贝);如果类中成员变量有指针类型并且有动态内存分配(将数据开辟到堆区),那么就一定要手写一个拷贝构造(深拷贝)

重点:深拷贝与浅拷贝

class Person
{
	public:
		Person() {}; //无参构造
		Person(int a,int b) : m_a(a),m_p(new int(b)) {}; //有参构造
		//编译器提供的拷贝构造,浅拷贝
		//完全的简单赋值,当有动态内存分配时就会出现问题。
		/*对象p中的m_p这个指针指向的是一个堆区地址,此时将这个地址赋值给这个对象的m_p这个指针
		此时有两个对象的成员指针指向同一个堆区地址,当这两个对象被销毁时,其在堆区开辟的内存要被释放
		但是两个对象中的成员指针却指向的是同一个堆区地址,就会造成同一个堆区地址被重复释放,
		因为一旦被释放一次过后该内存就成了非法内存,就没有权限再去操作了
		这就是浅拷贝带来的问题:堆区内存重复释放
		*/
		//Person(const Person& p) {m_a = p.m_a;m_p = p.m_p;}
		
		//解决办法:深拷贝
		Person(const Person& p)
		{
			this->m_a = p.m_a;
			this->m_p = new int(*p.m_p); //在堆区重新开辟一段内存,存放的数据一样,但不是同一块地址
		}

		~Person()
		{
			if(m_p)
				delete m_p;
			m_p = nullptr;
			cout << "Person的析构函数" << endl;
		}
	private:
		int m_a;
		int* m_p;
}

5.友元

  • 友元可以分为全局函数友元,成员函数友元,类友元
  • 友元可以访问类中保护权限和私有权限的成员
  • 友元需要用到关键字friend
class boy; //先声明一下这个类
class girl;

class Person
{
	//声明友元
	friend void func1(); //这个函数就可以访问该类protected和private权限的成员了
	friend class boy; //这个类作为该类的友元
	friend void girl::func3(Person& p1); //类girl中的成员函数func3作为该类的友元
	
	public:
		Person(){};
	protected:
		int m_c; //类内,友元,派生类可以访问
	private:
		int m_a; //类内,友元可以访问
		int m_b;
}

//全局函数做友元
void func1()
{
	Person p1;
	p1.m_a = 10;
	p1.m_c = 20;
}
//类做友元
class boy
{
	public:
		void func2(Person& p1)
		{
			p1.m_a = 10;
			p1.m_c = 20;
		}
}
//类成员函数做友元
class girl
{
	public:
		void func3(Person& p1)
		{
			p1.m_a = 10;
			p1.m_c = 20;
		}
}

6.对象模型

类是一个抽象的定义,只定义一个类并不会占用内存,只有创建具体的对象时,才会为其分配内存。类只是抽象出来的

  • 类成员
    一个类中有四种成员,分别为非静态成员变量,非静态成员函数,静态成员变量和静态成员函数。其中只有非静态的成员才属于类
    一个类或者一个类对象的所占内存大小是多少?
    对象模型:每一个实例化的对象,属于自己独有的只是非静态的成员变量,所以每一个类/对象的大小就是其中非静态成员变量的大小(内存对齐)。而对于非静态的成员函数,只存储一份,每个对象调用的都是同一份,那么每个非静态成员函数又怎么知道是哪个对象调用的自己呢?这就用到了this指针。对于静态成员变量和函数,它们不属于类和对象(静态成员函数内部没有this指针)。注意,一个空类/实例化的对象内存是1B

  • this指针
    每个非静态成员函数内都有一个隐藏的this指针,通过this指针编译器就知道是哪个对象在调用这个成员函数。哪个对象调用的这个成员函数,这个成员函数内的this指针就指向那个调用的对象。 使用return *this;返回调用函数的这个对象 如果是返回引用,那就是返回这个对象本体;如果返回值,那就是拷贝构造,返回这个对象的副本。(链式编程思想)

    this指针的本质:this指针的本质是一个指针常量(this == person* const this)也就是说this是一个常量,一个指针类型的常量。this指向的地址不可以改变,地址内的数据可以改变。哪个对象调用的这个函数,这个函数内的this指针就指向谁,不能改变指向。

  • const修饰成员函数(常函数和常对象)
    1.成员函数后面+const 是常函数:void func() const 常函数内不能修改非静态成员变量 。 如果成员变量前加mutable,则对于常函数和常对象都可以修改。
    常函数+const的本质:本来this指针为 person* const this,函数加上const成为常函数后,this指针变为const person* const this;此时this指向的地址不可以改变,地址内的数据也不可以修改。
    2.常对象:const person p;//p为常对象 常对象只能调用常函数 也可以修改mutable关键字的变量

#include <iostream>
using namespace std;

//静态成员变量和静态成员函数作为类成员 
class Person
{
public:
	Person() {
	}
	Person(int a,int b) : m_a(a),m_b(b)
	{
		cout << "Person有参构造" << endl;
	}
	
	~Person()
	{
		cout << "Person析构函数" << endl;
	}
	
	static void func()
	{
		//静态成员函数,不属于类,在全局区,所有成员对象共享一个函数,且静态成员函数只能访问静态成员变量
		//静态成员函数中是没有this指针的
		m_c = 600; 
	}
	
	int m_a;
	int m_b;
	static int m_c; //静态成员变量,不属于类,在全局区	所有成员对象共享一份数据 
};
//静态成员变量 类内声明类外初始化
int Person::m_c = 10; 

void test01()
{
	Person p(100,200);
	cout << p.m_a << endl;
	cout << p.m_b << endl;
	cout << "static m_c = " << p.m_c << endl;
	cout << "static m_c = " << Person::m_c << endl; //静态成员变量 通过类名作用域使用 
	cout << "sizeof(Person) = " << sizeof(p) << endl; //sizeof = 8,说明静态成员变量不在类上
	
	Person p2;
	p2.m_c = 50;
	cout << "p.m_c = " << p.m_c << " p2.m_c = " << p2.m_c << " person::m_c = " << Person::m_c << endl; //全部都被修改成了50 
	
	Person::func();
	cout << "p.m_c = " << p.m_c << " p2.m_c = " << p2.m_c << " person::m_c = " << Person::m_c << endl; //全部都被修改成了600
}

/*-----------------------------------------------------*/

class Base
{
public:
	Base() {
	}
	Base(int a,int b) : m_a(a),m_b(b)
	{
		cout << "Base有参构造函数" << endl;
	}
	
	~Base()
	{
		cout << "Base析构函数" << endl;
	}
	
	//成员函数和成员变量分开存储,每个类对象创建时会创建出一份成员变量,但成员函数只有一份,如何区分是哪个对象在调成员函数,this指针
	//每个非静态成员函数中有一个this指针(包括构造函数和析构函数),this指针指向调用该成员函数的对象
	//this指针可以拿到所有该对象所属的变量和函数,但不能拿到静态的成员变量和函数,因为静态成员变量和函数不属于类 
	
	void func()
	{
		this->m_a = 100;
		this->m_b = 200;
		cout << "m_a = " << this->m_a << " m_b = " << this->m_b << endl;
	}
	Base copy_Base()
	{
		this->m_a += 10;
		this->m_b += 20;
		return *this; //返回调用这个函数的对象,这里返回值是值返回,所以返回的是该对象的一个拷贝 这里会调用匿名拷贝构造 
	}
	Base& re()
	{
		this->m_a += 10;
		this->m_b += 20;
		return *this; //返回调用这个函数的对象,这里返回值是引用返回,所以返回的是这个对象本身 
	}
	
	int m_a;
	int m_b;	
};

class Base1
{
public:
		
	
};

void test02()
{
	Base1 b1;
	cout << "sizeof(Base1) = " << sizeof(Base1) << endl; //空类的对象大小是1 
	
	Base b(10,20);
	b.func(); //b这个对象调用非静态成员函数func,那么func内的this指针就指向b 
	
	cout << "sizeof(Base) = " << sizeof(Base) << endl;
	
	//this指针
	Base bb(10,20);
	bb.copy_Base().copy_Base().copy_Base(); //第一次调用结束后返回的是拷贝出来的一个bb副本,后面全是返回该次调用的拷贝 
	cout << "m_a = " << bb.m_a << " m_b = " << bb.m_b << endl; //m_a=20,m_b=40;说明返回的是拷贝,不是返回的bb本体 
	
	Base bb2(10,20);
	bb2.re().re().re(); //返回的是引用,所以*this返回的是本身 
	cout << "m_a = " << bb2.m_a << " m_b = " << bb2.m_b << endl; //m_a=40,m_b=80;说明返回的是本身 
	
	//this指针本质:this指针是一个指针常量,指针是一个常量,指针的值(指针指向的地址)不可以改变,地址内的数据可以改变  Base* const this = &bb; 
}

/*-------------------------------------------------*/
class Human
{
public:
	Human(){
	}
	Human(int a,int b,int c) : m_a(a),m_b(b),m_c(c)
	{
		cout << "Human有参构造函数" << endl;
	}
	
	~Human()
	{
		cout << "Human析构函数" << endl;
	}
	
	//常函数 
	void func() const
	{
		//this指针变从Human* const this 变为 const Human* const this
		//this指针指向的地址和地址内的数据都不可以改变
		
		//this->m_a = 200; error 常函数内不可以修改非静态成员变量
		this->m_c = 200; //如果非静态成员变量声明时前面加关键字mutable,那么常函数和常对象内依旧可以修改
		m_d = 300; //常函数内可以修改静态成员变量,因为this指针并不会拿到静态成员变量 
	}
	
	int m_a;
	int m_b;
	mutable int m_c; //加关键字mutable,即使是常函数和常对象依旧可以访问修改此变量 
	static int m_d;
};
int Human::m_d = 10;

void test03()
{
	Human h(10,20,30);
	h.func();
	cout << "m_a = " << h.m_a << " m_b = " << h.m_b << " m_c = " << h.m_c << " m_d = " << h.m_d << endl;
	
	//常对象
	const Human h1(10,20,30); 
	h1.func(); //常对象只能调用常函数 
	//h1.m_a = 100; error 常对象不可以修改非静态成员变量
	h1.m_c = 100; //但是,常对象可以修改用mutable关键字修饰的非静态成员变量 
	h1.m_d = 400; //常对象可以修改静态成员变量 
}

int main()
{
	//test01();
	//test02();
	test03();
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值