IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++:类与对象(适合初学 |较全面) -> 正文阅读

[C++知识库]C++:类与对象(适合初学 |较全面)

C++:类与对象(更新中)

参考自身学习的学习经验与Primer c++ 第五版的定义:类的基本思想是数据抽象与封装。
说得简单一些,那就是**:对事物的往上抽象** 称为
具体怎么解释呢,比如,对于我们大家而言,如果将我们抽象出来的话,那就是人。我们可以抽象成人 类。
再比如动物,比如猫,狗,猪,我们可以把他们抽象成为动物类。 正在学校学习的我们可以抽象成学生类。教师们也可以抽象成为 教师类。


1.1:类与对象的基本概念透析:

类的构成有属性、行为之分:
(初学C++对这些概念可能不太清楚,如果不仔细斟酌品味,到后面容易混乱)

属性:又被称为 数据
行为:又被称为 函数、方法

属性 与 行为 我们一般统称为 成员 ,所以又可以这样这样写:
属性, 称为: 成员属性、成员变量。
行为, 称为: 成员函数、成员方法

怎么理解属性与行为呢?
比如一个人类:Human
人,应该有他的名字,年龄等等,这些就叫做:属性。
行为呢? 吃喝拉撒 就是人的行为。

那么,啥叫 对象 ?(又称实例化)
再次举例,有了定义人类Human 以后,我们就可以实例化对象。
那么实例化我,又被称为 创建了一个 我 的对象。
然后根据类中的行为(吃喝拉撒)与属性(年龄、姓名),再次进行进一步的赋值。
比如创建了 我 这个对象以后,我名字叫张三。
然后再创建了 你 这个对象 ,你名字叫李四。


1.2:类(class)与对象在C++中代码的体现(Public):

代码如下(部分):

Class Human{
Public:
//属性:
string name;
int age;
//行为:
Void eat(){
cout<<"吃"<<endl;
};
Void drink(){
cout<<"喝"<<endl;
};
Void biubiubiu(){
cout<<"拉"<<endl;
};
Void pilipala(){
cout<<"撒"<<endl;
};
}

//以上就是类的定义 (先说一下public 先)
//然后在主函数main() 中实例化对象

int main(){
Human Zhangsan //创建了一个类名是Human 的对象 Zhangsan 
Human Lisi // 又可以理解就在Human类下实例化了一个lisi的对象。

//给Zhangsan赋名、年龄
Zhangsan.name="张三";
Zhangsan.age=18;

//李四:
Lisi.name="李四";
Lisi.age=19;

//张三要吃 那就在shell(控制台屏幕显示)中体现出来: 调用成员函数
cout<<Zhangsan.eat()<<endl;
}

1.3:访问权限

类在设计的时候, 成员属性与成员函数可以放在不同的权限下,用于控制:
①public:公共权限
②protected:保护权限
③private:私有权限

区别:
public:成员 类内可以访问,类外可以访问
protected:成员 类内可以访问,类外不可访问 子类可以访问
private:成员 类内可以访问,类外不可访问 子类不可以访问

代码如下:

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;

class  Human{
public:
	string house;//房子
protected:
	int money; //钱
	void fun_pro();
private:
	int bank_password; //银行卡密码
	void fun_pri();

public:
	
	//类内可以访问:
	void test1(){
		string house="别墅";
		int money=888;
		int bank_password=123;
	
	}

};


int main(void){
	Human son;
	son.house="海景房";
	//son.money=8888;  保护权限类外不可访问
	//son.bank_password=321;私有权限类外不可访问

	son.test1(); 
	//son.fun_pro();
	//son.fun_pri();

	//从以上代码可以知道,无论是成员变量也好,成员方法也好,都是有权限的.注释是属于类外访问,是非法的.


system("pause");
return 0;
}

1.4:结构体struct 与 类class的区别:

其实是一样的,只是有一点区别,那就是在默认的权限不一样.
struct:默认权限是公有
class:默认权限是私有

class Human{
	int name;  //属于私有权限private
};

struct Human{
	int name;//属于公共权限public
};

1.5:成员属性设为私有的意义

优点1:可以自己控制读写权限(按需定义读写)
优点2:对于写权限,可以检测数据的有效性

代码如下:(部分 代码是从文档写下来然后拷贝过来的,应该有些错误 但不影响)

Class Human{
Private:
String h_name; //设置可读可写
Int h_age;//设置可读不可写
String h_lover //设置可写不可读

//以上的设置在public中体现:
Public:
String getName(){
return h_name;
}
Void setName(string name){
h_name=name;
}
Int getAge(){
Return h_age
}

Void setLover(strng lover){
h_lover=lover
}
}

//以上就是成员属性设置私有的第一个优点,可以按需定义读写方法.
//检测数据的有限性如何体现呢?比如我们在public方法中再次定义一个成员方法,将age设置为可读可写的,即再多加一个setAge()方法即可:

Public:  //在前面写了public 后面仍然可写
Void setAge(int age){
If(age<0 ||age>150){
Return;
}
h_age=age;
};

1.6:构造函数与析构函数

构造函数:
1.没有返回值 不需要void;
2.函数名与类名一致;
3.形式:有参\无参\重载
4.创建对象时,会自动调用且只调用一次.

析构函数:
1.进行堆区清理操作
2.无返回值 不需要void
3.函数名与类型相同 只需要前面加上:~
4.不能够带参,不可以重载
5.对象销毁前会自动调用析构函数,只调用一次.

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;


class Human{
public:
	//无参构造函数
	Human(){
		cout<<"无参构造函数的调用"<<endl;
	}

	//有参构造函数
	Human(int a){
		age=a;
		cout<<"有参构造函数的调用"<<endl;
	}

	//析构函数
	~Human(){
	cout<<"析构!"<<endl;
};
public:
	int age;

};

//无参构造调用
void test01(){
		Human wucan;
};

void test02(){

	Human youcan(10);
	cout<<youcan.age<<endl;
};

int main(void){

	test01; //无参构造不需要加()
	test02();
	
system("pause");
return 0;
}

1.7:构造函数的分类:

构造函数的分类:
①按参数分: 有参构造 无参构造
②按类型分: 普通构造 拷贝构造

构造函数的调用方式:
①括号法
②显示法
③隐式转换发

无参与有参(代码与上面类似 为了容易区分 分多个区域写):

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;

//构造函数的分类:
//	①按参数分: 有参构造 无参构造
//	②按类型分: 普通构造 拷贝构造
//
//构造函数的调用方式:
//	①括号法
//	②显示法
//	③隐式转换发

//①按参数分: 有参构造 无参构造
class Human{

public:
	
	Human(){
		cout<<"Human 无参"<<endl;
	}

	Human(int a){ 
		age=a;
		cout<<"Human 有参"<<endl;
	}
private:
	int age;


};

void test01(){
	Human a;
};

void test02(){
	
	Human b(10);
}


int main(void){
	
	test01;
	test02();


system("pause");
return 0;
}

拷贝构造函数: 顾名思义 就是用于复制(拷贝)一个对象 的函数

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;

//构造函数的分类:
//	①按参数分: 有参构造 无参构造
//	②按类型分: 普通构造 拷贝构造

//构造函数的调用方式:
//	①括号法
//	②显示法
//	③隐式转换发

//②按类型分: 普通构造 拷贝构造
class Human{

public:
	
	//有参构造(无参\有参都属于普通构造)
	Human(int a){ 
		age=a;
		cout<<"Human 有参"<<endl;
	}

	//拷贝构造函数:就是拷贝一个完全一样的对象
	//const是常量.
	//const是为了防止让本体发生改变,其次还要按照引用的方式传进来
	//形参son命名随便写都可以
	Human(const Human &son){
		age=son.age;
		cout<<"Human 拷贝"<<endl;
	}


public:
	int age;
};


//测试方法   
void test03(){
	
	Human b(10); //有参构造函数  年龄为10的b
	Human son(b);//拷贝构造函数(其实我觉得叫拷贝对象构造函数更贴切) 意思是:从对象b中拷贝一个age值相同的名为son的对象出来.

	cout<<b.age<<endl;
	cout<<son.age<<endl; //值与这个b是一样.
}

int main(void){
	
	test03();


system("pause");
return 0;
}

构造函数调用方法: 随便你用哪个都行 看你喜欢

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;

//构造函数的分类:
//	①按参数分: 有参构造 无参构造
//	②按类型分: 普通构造 拷贝构造


//构造函数的调用方式:
//	①括号法
//	②显示法
//	③隐式转换发



class Human{

public:
	
	Human(){
		cout<<"Human 无参"<<endl;
	}

	Human(int a){ 
		age=a;
		cout<<"Human 有参"<<endl;
	}

	Human(const Human &son){
		age=son.age;
		cout<<"Human 拷贝"<<endl;
	}

private:
	int age;

};

//括号法 
void test_1(){
	Human a;
	Human b(10);
	Human c(b);

	cout<<"括号法-------"<<endl;

}


//显示法
void test_2(){
	Human aa;
	Human bb=Human(100);
	Human cc=Human(bb);

	cout<<"显示法-----------"<<endl;
}

void test_3(){
	Human bbb=1000;  //有参构造 等同于 Human bbb=Human(10);
	Human ccc=bbb;	 //拷贝构造 等同于 Human ccc=Human(bbb);
	cout<<"隐式转换法-------------"<<endl;
}


int main(void){
	test_1();
	test_2();
	test_3();


system("pause");
return 0;
}

运行图:
在这里插入图片描述


1.8: 拷贝构造函数的调用时机

题目理解起来可能有些模糊.
意思就是:
编译器会在什么时候调用 拷贝构造函数

时机①:使用已有的对象用来初始化新的对象
时机②:以值传递的形式给函数参数传值
时机③:以值的方式返回局部对象
时机④ 对象数组的初始化列表中,使用对象

代码如下: 有注释 应该不会看懵

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;


class Human{
	
public:

	int age;

	Human(){
		cout<<"无参构造函数"<<endl;
	}

	Human(int a){
		age=a;
		cout<<"有参构造函数"<<endl;
	}
	
	Human (const Human &son){
		age=son.age;
		cout<<"拷贝构造函数"<<endl;
	}



	~Human(){
		cout<<"析构函数调用"<<endl;
	};
	
	
	
};

//时机①:使用已有的对象用来初始化新的对象

/*说明:调用拷贝构造函数规则:调用函数时,实参是对象,形参不是引用类型的时候会调用拷贝构造函数;
如果函数的形参是引用类型,就不会调用拷贝构造函数*/

void test01(){
	Human p1(10); //调用有参构造函数 传10进去
	Human p2(p1);//调用拷贝构造函数


	//测试是否拷贝成功
	cout<<p1.age<<endl; 
	cout<<p2.age<<endl;

}


//时机②:以值传递的形式给函数参数传值   (值传递的本质是会创建一个临时的副本出来,这个临时的调用完会消失)

//调用这个方法的作用就是为了让大家清晰地了解到:当 实参q1传给形参x的时候,会调用一个拷贝构造函数
void demo2(Human x){
}

void test02(){

	Human q1; //创建对象q1;
	demo2(q1);

}


//时机③:以值的方式返回局部对象
Human demo3(){
	
	Human a1; //创建一个局部对象  //调用默认构造函数
	
	return a1; //这个一个值的对象 不会返回 Human a1 这个对象  ,他会拷贝一个新的对象出来,返回到调用它的函数

}

void test03(){
	Human a = demo3();  //human a 对象来接收demo3() 的数据
}


//④ 对象数组的初始化列表中,使用对象  也会调用默认构造函数
//不多作解释 知道就好:
// Human f1,f2,f3,f4  创建了4个对象  流星花园F4
// Human  cosplay_f4[4]={f1,f2,f3,f4} //定义了一个数组 
//初始化使用了对象.(理解为有4个精神精神小伙cos流星花园)但此时但会调用4个拷贝构造函数.

int main(void){

	cout<<"调用test01方法:(这是一个最常见的拷贝函数构造调用方法)"<<endl;
	test01();

	cout<<"----------------------------------------------------------"<<endl;
	
	cout<<"调用test02方法:(以值传递的形式给函数参数传值)"<<endl;
	test02();

	cout<<"----------------------------------------------------------"<<endl;
	cout<<"调用test03方法:(以值的方式返回局部对象)"<<endl;
	test03();


system("pause");
return 0;
}

代码实现图:
在这里插入图片描述

1.9: 构造函数调用规则:

默认情况下,C++编译器至少会给一个类添加3个构造函数:

①默认构造函数(无参的,函数体为空的)
②默认析构函数(无参的,函数体为空的)
③默认拷贝构造函数,对属性进行值的拷贝.

规则:
①如果定义了有参构造函数,C++不会提供无参构造函数,但是会提供默认构造函数.

②如果定义了拷贝构造函数,C++不会提供其他构造函数了(包括默认构造\默认析构函数\默认拷贝构造函数)

可以理解为:你去一家面馆吃饭,你跟老板说来碗热面. (你没有提要求),老板就会给你热面放香菜进去. 但是如果你提了要求,说来碗热面不要香菜,那老板就不会给你放香菜.


1.10: 浅拷贝与深拷贝:

浅拷贝:简单的赋值拷贝,带来了问题就是 堆区内存的重复释放
深拷贝:在堆区重新new空间,进行拷贝赋值.

伪代码:

Human a1; //创建对象
human a2(a1);//会调用一个拷贝构造函数

假设我们在前面给a1这个对象new了一块空间:
int *arrd;  
对象a1的这个成员会指向一个地址为:0x0001的堆区

拷贝出来一个a2的时候 ,浅拷贝就是一个完全的赋值拷贝,
对象a2的arrd成员也会指向地址为:0x0001的堆区;

我们知道析构函数会清理操作,
假设析构函数被调用进行清理的时候  
那么就会 重复清理某一个堆区(这是非法的)

所以就需要在实现拷贝构造函数的时候,需要自己定义:

Human(const Human &son){
	arrd=new int(*son.addr);//意思就是为调用拷贝构造函数的那个对象给它重新创建一个堆区. 
	
}
	如果你不为它重新创建一个堆区,系统会默认调用这个代码:
	addr=son.addr;  
	//这是一个简单的赋值操作,这会让son.addr与a1.addr共同享有一个堆区.(这是不明智的)

2.1: 初始化列表:

初始化列表:
作用:初始化属性
语法:构造函数():属性1(value1),属性2(value2)…{};

#include<iostream>
#include<string>
#include<Windows.h>
using namespace std;



//一般的初始化操作:
class Human{
public:
	int p_a;
	int p_b;
	int p_c;

	Human(int a,int b,int c){
		p_a=a;
		p_b=b;
		p_c=c;
	}
};

//初始化列表:
class Super{
public:
	int m_a;
	int m_b;
	int m_c;

	//不带参:Super():m_a(10),m_b(20),m_c(30){};
	Super(int a,int b,int c):m_a(a),m_b(b),m_c(c){};

};


void test01(){
	Human s1(10,20,30);//调用有参构造
	cout<<"---调用有参构造函数(传统调用)---"<<endl;
	cout<<s1.p_a<<endl;
	cout<<s1.p_b<<endl;
	cout<<s1.p_c<<endl;

	cout<<endl;
}


void test02(){
	Super s2(10,20,30);
	cout<<"---调用有参构造函数(初始化列表)---"<<endl;
	cout<<s2.m_a<<endl;
	cout<<s2.m_b<<endl;
	cout<<s2.m_c<<endl;
}
int main(void){

	test01();
	test02();


system("pause");
return 0;
}

实现图:
实现图


2.2: 类对象作为类的成员:

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:05:39  更:2022-04-09 18:07:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 23:42:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码