C++学习笔记4(类和对象之对象特性)

对象特性

对象的初始化和清理

对象的初始化—>构造函数

(主要作用在于创建对象时为对象的成员属性赋值(无需手动调用))
语法:类名 (){}

  • 构造函数没有返回值且不写void
  • 函数名与类名相同
  • 可以有参数,可以重载
  • 程序在调用对象时会自动调用构造函数且只会调用一次
对象的清理—>析构函数

在析构函数里面程序员手动释放堆区的数据
(主要作用在于对象销毁前系统自动调用,执行清理工作)
语法:~类名(){}

  • 析构函数没有返回值且不写void
  • 函数名与类名相同最前面加~
  • 析构函数不允许有参数,不可以发生重载
  • 程序在对象销毁前会自动调用析构函数且只会调用一次

这两函数都由编译器自动调用,程序员不提供的话编译器会自己提供空实现的构造函数和析构函数

构造函数和析构函数通常都在public作用域中

构造函数的分类和调用

分类
1、(按参数分)

有参构造函数、无参构造函数

2、(按类型分)

普通构造函数、拷贝构造函数

拷贝构造函数:
class Person{//类名
	Person(const Person &p){//拷贝构造函数|const是为了保护p不被修改
		//自己属性 = p.属性名
	}
};
调用
1、括号法:

(1)类名 对象名;//默认构造函数调用
这里不能加括号,加上括号后编译器会把这个当作函数声明
(2)类名 对象名(普通参数);//有参构造函数调用
(3)类名 对象名(对象参数);//拷贝构造函数调用

2、显示法:

(1)类名 对象名;//默认构造函数调用
(2)类名 对象名 = 类名(参数);//有参构造调用
(3)类名 对象名 = 类名(对象参数);//拷贝构造函数调用
类名(参数);//匿名对象
不要用拷贝构造函数初始化匿名对象
(编译器会认为 类名(对象参数)等于 类名 对象名,会报错说重定义)

当匿名函数所在的行执行结束后,系统会立即回收匿名函数

3、隐式转换法:

(1)类名 对象名 = 参数;(相当于显示的(2))
(2)类名 对象名 = 对象参数;(相当于显示的(3))

拷贝构造函数的调用时机

1、使用一个已经创建完毕的对象来初始化一个新对象
2、值传递的方式来给函数参数传值
3、以值方式返回局部对象

class Person {
public:
	Person() {//无参构造函数
		mAge = 0;
	}
	Person(int age) {//有参构造函数
		mAge = age;
	}
	Person(const Person& p) {//拷贝构造函数;
		mAge = p.mAge;
	}
	//析构函数在释放内存之前调用
	~Person() {//析构函数
	}
public:
	int mAge;
};

//1. 使用一个已经创建完毕的对象来初始化一个新对象
void test01() {

	Person man(100); //p对象已经创建完毕
	Person newman(man); //调用拷贝构造函数
	Person newman2 = man; //拷贝构造

	//Person newman3;
	//newman3 = man; //不是调用拷贝构造函数,赋值操作
}

//2. 值传递的方式给函数参数传值
//相当于Person p1 = p;
void doWork(Person p1) {}
void test02() {
	Person p; //无参构造函数
	doWork(p);
}

//3. 以值方式返回局部对象
Person doWork2()
{
	Person p1;
	cout << (int *)&p1 << endl;
	return p1;
}

void test03()
{
	Person p = doWork2();
	cout << (int *)&p << endl;
}


int main() {

	//test01();
	//test02();
	test03();

	system("pause");

	return 0;
}
构造函数的调用规则

默认情况下,c++的编译器至少给一个类添加3个函数:
1、默认构造函数(无参,函数体为空)空实现
2、默认析构函数(无参,函数体为空)空实现
3、默认拷贝函数,对属性进行值拷贝 值拷贝

构造函数调用规则:

  • 如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会提供其他构造函数
深拷贝和浅拷贝

浅拷贝:简单的复制拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
利用编译器提供的拷贝构造函数是浅拷贝
浅拷贝的问题:对区内存重复释放
解决浅拷贝的问题:自己编写拷贝函数·,申请内存,即深拷贝
当成员变量有指针时要重写拷贝构造函数和析构函数
在这里插入图片描述

初始化列表

1、作用:C++提供了初始化列表语法,用来初始化属性
2、语法:构造函数():属性1(值1),属性2(值2)… {}
注意冒号

类对象作为类成员

例如

class A {}
class B{ 
   A a;
}

B类里面,用A的对象做类成员
先调用A类的构造函数后调用B的构造函数
先调用B类的析构函数后调用A的析构函数

静态成员

1、定义在成员变量和成员函数前加上关键字static
2、分类:静态成员变量,静态成员函数

静态成员变量
  • 所有对象共享同一份数据
    当a对象修改了该静态成员变量,b对象这边的静态成员变量值也改变
  • 在编译阶段分配内存
  • 类内声明,类外初始化
    注意!静态成员变量必须初始化
    静态成员变量也有访问权限
class Person
{
public:
	
	static int m_A; //声明
};
int Person::m_A = 10;//初始化

静态成员变量可以通过对象名访问也可以通过类名进行访问
通过类名访问的语法是 类名::静态成员变量名

静态成员函数
  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

静态成员函数可以通过对象名访问也可以通过类名进行访问
静态成员变量也有访问权限

C++对象模型和this指针
1、成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象

class Person {
public:
	Person() {
		mA = 0;
	}
	//非静态成员变量占对象空间
	int mA;
	//静态成员变量不占对象空间
	static int mB; 
	//函数也不占对象空间,所有函数共享一个函数实例
	void func() {
		cout << "mA:" << this->mA << endl;
	}
	//静态成员函数也不占对象空间
	static void sfunc() {
	}
};
//空对象占用空间为:1(为了区分不同的空对象占内存的位置)
//每个空对象也应该有一个独一无二的内存地址

int main() {

	cout << sizeof(Person) << endl;

	system("pause");

	return 0;
}
2、this指针

(1)this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可

(2)this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this
    这里的this时指针,用"->“,Java里的this用” . "

this指针是指针常量,不能修改指向,可以修改指针指向的值

class Person
{
public:

	Person(int age)
	{
		//1、当形参和成员变量同名时,可用this指针来区分
		this->age = age;
	}

	Person& PersonAddPerson(Person p)//返回值类型要用引用
	{
		this->age += p.age;
		//返回对象本身
		return *this;
	}

	int age;
};

void test01()
{
	Person p1(10);
	cout << "p1.age = " << p1.age << endl;

	Person p2(10);
	//链式编程思想
	p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1);
	cout << "p2.age = " << p2.age << endl;
}

int main() {

	test01();

	system("pause");

	return 0;
}
空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意成员函数内部有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

class Person {
public:

	void ShowClassName() {
		cout << "我是Person类!" << endl;
	}

	void ShowPerson() {
		if (this == NULL) {
			return;//加上 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后我们称为这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改,也可以通过常对象修改

补充
函数的()后面加const表示此时函数里使用的this指针不能改变指向,也不能改变指向的值

常对象:
  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值