C++大杂烩

C++大杂烩,第1张

C++大杂烩

C++ 之我的大杂烩
  • c++基础
    • 头文件
    • 调试时界面一闪而过
    • cin 输入
    • cout输出
    • gmpz函数
    • endl-高级换行
    • using namespace std 空间命名
    • 高级注释符-#if 0 ... #endif
    • typedef 创建新的自定义类型的名字
    • 取数据类型LOWORd(), HIWORd(), LOBYTE(), HIBYTE()
    • 指针 int* ptr
    • 结构体 struct My_Type
    • 引用 int &=xx
    • 函数升级版
      • __1-1导入部分__
        • ___2-1访问权限___
      • ___3-1struct与class的区别___
    • ___1-2-对象的初始化与清理___
      • __1-2-1-导入__
        • _构造函数_
      • _析构函数_
      • ___1-2-2构造函数的分类以及调用___
      • 大致框架
      • 无参数构造
      • 有参数构造
      • 普通构造
      • 拷贝构造
      • 调用方式-括号法
      • 调用方式-显示法
      • 调用的隐士法
    • ___1-2-3-拷贝构造函数调用的时机___
    • ___构造函数的调用规则___
    • 深度拷贝与浅拷贝
      • 浅拷贝
      • 深度拷贝
    • 初始化列表给类赋初值
    • 类对象作为类成员(嵌套式类)
    • 静态成员
    • 成员变量与成员函数分开存储
  • C++一些高级的应用
    • 暂停 pause
    • 清屏 cls

c++基础 头文件

#include
using namespace std;
i input
o output
stream 流

调试时界面一闪而过

你要高大上点
cin.clear(); // 清空缓存
cin.sync(); // 清空缓存
cin.get(); // 接收键盘输入
再组后用这几个函数就

你要简单点?
在最后写

getchar();

然后最后输入一个char的字符来结束调试

你要专业点?

//引入头文件
#include
或者
#include

//在代码最后写
system("pause");
//于是调试到最后需要你enter才会结束调试

不管它是什么类型,输出就可了

cin 输入

其实输入的话,相比C语言,它少了一些格式化的 *** 作

cin>>x;
"不管x是什么类型,只管往里面输入就"

std::__cxx11::basic_string::basic_string(xxx, D_ptr_1[1
std::__cxx11::basic_string::end(xxx)
像这些C++函数,你只需要看最后一句就可以了!最后一句就是它调用的函数

这些函数一般可以实现指针的传递,动态内存分配…

如何分析一个陌生的东西,还比

cout输出
cout << dx;
 cout<<"Please input an int number:"< 

不同于C语言还要去格式化一下%d,%p,%c
相比与C语言的输出

cout< 
gmpz函数 

__gmpz_init_set_str(v5, “65537”, 10LL);
东西,他确实是一个函数,点进去看,啥也没有,其实这更加确定了它是一个系统函数!系统函数就去百度呗,然后看它是什么作用

endl-高级换行

使用 endl 时会刷新缓冲区,使得栈中的东西刷新一次,而不是仅仅换行的 *** 作
这么比喻

std::cout << std::endl;

是由2个部分构成,一个换行 *** 作,一个刷新缓冲区的 *** 作构

//flush应该是刷新的意思
td::cout << 'n' << std::flush;

或者说是这个

std::cout << 'n'; std::fflush(stdout);

其实有时候你不需要换行与刷新的 *** 作…于是这个endl的效率当然没’n’它的效率高,造成速度低慢…取决于你对程序的严谨性,想用endl就用呗

using namespace std 空间命名

using namespace std 到底是什么意思??
声明一个命名空间(称之为房子)的意思
就是声明这个房子的名字…这个房子就是我的了.它的名字ID是我确认的

using namespace std 其中的std是官方的房子…也就是说谁都可以用它
啰嗦了一大堆,该说回来了
std::
std 表示是 C++ 的标准命名空间,就是编译系统自带有的,按 C++ 标准定义好了的。
Eg:
我定义2个房子
using namespace 李四家
using namespace 张三家

我分别再仓库里面放了2个一模一样名字的变量-看门狗Single_Dog
using namespace 李四家 : : Single_Dog
using namespace 张三家 : : Single_Dog
名字都是Single_Dog看门狗…但是他们的主人不一样,所属对象不一样

比如,在使用输出 std::cout 时,如果它达不到我们想要的效果,我们也可以自己定义一个名字空间。
取名 myspace,再在这个空间里写一个 cout 函数来实现。调用时,就成了 myspace::cout

高级注释符-#if 0 … #endif

我们可以使用 #if 0 … #endif 来实现注释,且可以实现嵌套,格式为:
#if 0
code代码段
#endif
那里的
0好比一个开关…0代表它是注释,关闭执行//#if 0 来屏蔽测试代码。
1好比打开开关…执行注释中的代码…//#if 1 来执行测试代码
#if xx
xxx可以条件语句。
下面的代码
如果 Button
条件为 True 执行 code1 ,
条件为 False执行 code2
#if Button
code1
#else
code2
#endif

#include
using namespace std;
int main()
{
//#与if挨在一起
//#endif也在挨在一起
	#if 0
	printf("you find men");
	#endif
	printf("%s","you catch me");
	cin.get();
}
typedef 创建新的自定义类型的名字

您可以使用 typedef 为一个已有的类型取一个新的名字。

typedef type(类型) newname(名字);

Eg:
typedef char dqx_zj;
于是以前你用

char var

现在你可以

dqx_zj var

于是你就把自定义的名字换了一下罢了

取数据类型LOWORd(), HIWORd(), LOBYTE(), HIBYTE()

LOWORd(), HIWORd(), LOBYTE(), HIBYTE()
这些数据可能对针对与4字节的对象,好比一个int型的

LO是低,反之HI是高

LOWORD意思就是取4字节的低位2字节,毕竟WORD是2字节的
HIWORD意思就是取4字节的高位2字节
LOBYTE就是默认取低位2字节中取低位1字节
HIBYTE就是默认取低位2字节的高位1字节

其实他们都有定义的
我们一LOBYTE与HIBYTE为例子

#define LOBYTE(w)           ( (BYTE) ( (  (DWORD_PTR) (w) )       & 0xff))
#define HIBYTE(w)           ( (BYTE) ( ( ((DWORD_PTR) (w) ) >> 8) & 0xff))

上面的w就是你的参数,
然后你自己运行一下代码

#include   
#include   
#include
  
int main()  
{    
    unsigned int data = 305419896;//0x12345678  
    unsigned short Two_Byte_High= HIWORD(data);    // 取高16位  
    unsigned short Two_Byte_Low_= LOWORD(data);    // 取低16位  
  
    // result: 0x1234  
    printf("0x1234 5678 的  高位2字节 0x%xn",Two_Byte_High);
    // result: 0x5678
    printf("0x1234 5678 的  低位2字节 0x%xn",Two_Byte_Low_);  
      
    unsigned char One_Byte_High = HIBYTE(data);    // 取高8位  
    unsigned char One_Byte_Low_ = LOBYTE(data);    // 取低8位  
    // result: 0x56
    printf("0x1234 5678 的  高位1字节 0x%xn",One_Byte_High);    
    // result: 0x78  
    printf("0x1234 5678 的  低位1字节 0x%xn",One_Byte_Low_);   
	system("pause");
    return EXIT_SUCCESS;//#define EXIT_SUCCESS    0

}
指针 int* ptr

1-1-导读
指针==地址,

  • 定义指针

类型 * 指针的名字

Eg
int *my_ptr

  • 取地址:

指针 = &变量的名称
Eg:
my_ptr=&my_var;

  • 使用指针

_* _指针名字

  • 重点
    我们可以用指针间接访问与修改它

1-2-指针长度

在32位的 *** 作系统下,指针的长度就是4字节
在64位的 *** 作系统下,指针的长度就是8字节
32位的指针 0x40245632
64位的指针 0x1234567812345678

1-3-空指针

当地址是0就是空指针,别且它是不可以被访问的
可以用来初始化
NULL就是啥也没有的意思


小插曲


Eg:

int *p=NULL

另外我们还有一种指针初始化的方式

这里有一个强制类型转化


1-4总结



1-5-野指针


开了一间房却去了另外一间房子

上面p地址指向一块未知区域


2-const修饰指针


分为3种类型
常量指针
指针常量
以上2种的mix


2-1第一组类型:常量指针

const修饰指针 叫常量指针
const在前,类似于
const(int * My_Ptr)于是指向的数据是无法改变的

const int * my_ptr
常量+指针
const紧挨着int,int数据不可改

它有什么特性?

  1. 它指向地址对应的数据是不可以修改的…意思就是好比pointer指向数据var=100,那么我们无法通过const指针去修改数据100
  2. 虽然是const,但是它可以改变指向的地址,好比它可以指向A也可以指向B,就是无法改变它指向的地址对应的数据`

    红框数据不可修改…黑色箭头是合法指向

2-2第二种类型:指针常量

类似与int * (const My_Ptr)

int * const My_Ptr
指针+常量
const 紧挨*指针
指针不可修改

特点
指向不可修改
指向的值可以修改


红色不可指向
黑色数据可修改

2-3类型三 -->指针常量与常量指针的混合体

const int * const My_ptr

指向的数据与指针都不可以修改


关于指针的调用
*pre不仅仅是一个值,而是对应地址的值…我们改变它,其实是改变它对应地址的值


结构体 struct My_Type


1-1初级部分



如何理解这个自定义
我们定义一个数据类型,名字叫xxx
其实这个xxx类似是一些类型的集合
我们可以通过这个xxx类型调用那些已知类型

上代码

struct My_Type
{
	int Age;
	double High;
	double Weight;
	char * name;
};

于是你定义了一种类型,名字叫做My_Type,这种类型可以与Int,char
,double…等类型平起平坐…
比如你可以int var当然也可以My_Type var
你在调用var变量是会有所不同

可以看出.你自己新定义的类型My_Type其实是里面int,double,char的集合
关于
1-2结构体的声明会有3种方式
无论哪种法式,你都要新定义类型

struct My_Type
{
	int Age;
	double High;
	double Weight;
	char * name;
};

请注意
结构体声明结束后会有一个分号;以表示结束的意思
对于C++类的话也是一样的,最后都会有个分号;结尾

再是

1-1-1方式一

My_Type var;
var.Age=20;
var.High=165;
var.Weight=116;
var.name="D9x";

1-1-2方式二

My_Type var={20,16,116,"D9x"};

1-1-3方式三

struct My_Type
{
	int Age;
	double High;
	double Weight;
	char * name;
} var;

2-1结构体与数组


其实它的结构体定义还是一样的…只不过用它作为类型时,我们可以去定义一个数组罢了
首先还是一样的

struct My_Type
{
	int Age;
	double High;
	double Weight;
	char * name;
} var;

然后我们要去
2-2定义一个数组

My_Type MyArr[3]=
{
	{18,178,166,"一号"},
	{19,189,123,"二号"},
    {20,168,144,"三号"},
}

2-3如何访问它?
同样是用点的 *** 作符

My_Arr[i].name
My_Arr[i].Age
My_Arr[i].Weight

如何遍历结构体数组,其实和一般的数组遍历其实是一样的

for(int i=0;i<3;i++)
	printf("%d %d %d %d",My_Arr[i].Age,My_Arr[i].Weight,My_Arr[i].High);

3-1结构体指针


先给一个图

我们这里要
3-2引入一个 *** 作符->
它可以指向结构指针对应的数据
比如ptr->Age,所以有了->,我们就不再需要*来指向数据了
其实的话…其它的定义都是一样的
先还是定义结构体

struct My_Type
{
	int Age;
	double High;
	double Weight;
	char * name;
} ;

然后是
3-3定义指针?
指针类型是什么?
当然是那个集合体的类型,也就是你自己定义的类型…

My_Type var={20,16,116,"D9x"};
My_Type *My_Pt=&var;
//于是可以调用指针
printf("%d %s",My_Ptr->Age,My_Ptr->name);

4-1结构体嵌套结构体


其实与函数调用差不多吧…
函数里面又去调用另外一个函数
这里其实涉及一个逻辑的包含关系
好比爸爸比儿子大一级,同时呢爸爸与儿子的属性又是差不多的…因为他们都有age,name,weight.high

上代码

struct chld
{
	int Age;
	double High;
	double Weight;
	char * name;
} ;
//其实上面定义了一个子结构
//下面定义父结构
struct Father
{
	int Age;
	double High;
	double Weight;
	char * name;
	chld son;//这里定义了一个嵌套结构体
};

调用的话也很简单…

Father Father_var;
Father_var.Age=45;
Father_var.high=160;
//....
Father_var.son.Age=18;
Father_var.son.name="D9x";

值得我们注意的是…子结构一定要在父结构之前先定义…当然这是一个相对关系…


5-1结构体做函数的参数


其实可以类比一下swapt函数,就是你自己之前写的那个函数…里面涉及了一些关于值得传递与函数地址得传递
你传进去的是你自定义得类型…是一个类型的集合
上代码

struct Father
{
	int Age;
	double High;
	double Weight;
	char * name;
};
Father my_arr={12,150,80,"哆啦"};
void fun1(father* data)
{
	data->name="D9x";
	//这里通过了地址改变了实参的值
	printf("%d %s",data->Age,data->name);

}

void fun2(father data)
{
	printf("%d %s",data->Age,data->name);
	//如果这里去改变data的值的话,不会改变实参的值
}

6-1结构体中的const


其实这个功能就是为了不然使用者修改数据罢了
来一个图片你且讲解一下

在上面中,我们用到了函数的打印…为什么要传递进去指针而不就传递值进去,这2种方式的效果是一样的,但是资源的消耗却不一样

如果你传递值进去的话…函数的调用其实是拷贝数据…你调用1000次,拷贝1000次,会占用很多内容空间
如果你传递指针进去…你函数调用你的那一个地方的数据,你调用1000次,只不过每一次用了8字节/16字节呃指针罢了,然后大家都指向那一个地方的数据…大大节省了空间


7-1关于嵌套结构体的应用


我们来看看这2个结构体

struct Person
{
	//姓名 
	string Name;
	//性别
	int Sex;
	//年龄
	int Age;
	//电话
	string Phone;
	//地址
	string Location; 
}; 
//通讯录结构体
struct Boook 
{
	//联系人-数组
	people P_arr[max];

	//输入有多少个人
	int index;
	 
};

这是通讯里相关结构体
通讯录Book是一个类型集合
它包含了另外及一个结构体Peoson的数组与一个index的索引
Person结构体又包含了一些联系人的属性


指向indx就Book.index
指向Person就Book.Person[i].Age
如果用指针的话
好比把Book给指针化了
传参的时候&Book
然后访问的话
访问index就Book->index
访问sex就Book->Person[i].sex
如果访问index对应的sex就Book->Person[Book->index].sex

引用 int &=xx

其实–>给变量起别名


1-1导入部分


语法

数据类型- &-now_name = old_name
-----int ------&-now_var—=-old_var;

上代码

#include
#include

using namespace std;

int main()
{
	int old=10;
	int &now=old;
	
	cout<<"old is "< 

可以看出,

2个变量都访问了同一个值
更改其中一个变量其实等同于更改对应的all变量


1-2注意事项


引用时必须被初始化

int &now;(错误)
int &new=old;(正确)

引用不可以更改指向,就是引用指向了A,就不能再次指向B

int& now=pre;
now=var_Mg;(这是一个个赋值 *** 作,正确)
&now=var_Mg;(这是在修改引用的对象,错误)

2-1引用做函数的参数


以交换的函数为例子

  1. 值传递
  2. 指针传递
  3. 引用传递

引用传递

void swap(int& x,int& y)
{
	int temp=0;
	temp=x;
	x=y;
	y=temp;
}
swap(a,b);
这里的传入参数
好比 int &x=a, int &y=b;

3-1引用做函数的返回值



代码


#include
using namespace std;
 
//不能返回局部变量的引用
int& fun1()
{
	int a=100;
	return a;
}
//可以返回全局区变量的引用
int& fun2()
{
	static int b=200;
	return b;
}

int main()
{
	//不能返回局部变量的引用
	int& ret=fun1();
	cout<<"第1次调用,本来是100,现在是 "< 

static int b=200;静态变量的生命周期要长一些,因此不会在栈里面,也不会被清空
fun2()=300;这里的fun1()的返回值是int&类型,所以的话,如果做左值,会改变引用的对象


4-1引用的本质



它的指向不可以修改,指向的单元数据却可以修改

int& now=pre;
等同于
int *const now=⪯

在调用引用时好比

now=1100;
等同于
*now=1000;

5-1常量引用



好比

const int * const ptr;

引用常量

const int & now=10;
等同于
int Buff=10;
const int &now = temp;

在函数中的常量引用

void fun(const int& now)
{
	now=100;(编译器会报错,因为它不可修改)
	cout<<"valus is "< 
函数升级版 

1-1函数的默认参数


我们可以对函数不传入参数,先前就给他一些默认的参数
如果我们要传入也可以…接受一定的顺序传入

void fun(int a=10,intb=20,int c=30);
再调用的时候
fun(70,80);
那么的话,参数就会按着从前往后的顺序
a=70,b=80,c=30

注意事项

  • A.我们的默认参数的分布有一定的规则
  1. 默认参数都不去初始化
  2. 如果初始化其中一个,那么它后面的都要去初始化
  • B.声明函数与定义函数只有其中之一可以去初始化

情况A

fun(int a.int b, int c)(正确)
fun(int a=1,int b=2.int c=3)(正确)
fun(int a,int b=1,int c=2)(正确)
fun(int a=1,int b,int c)(错误)

情况B

函数声明
void fun(int, int);
函数定义
void fun(int a,int b)
{
	xxxxx;
}

于是在使用时

我们可以
void fun (int a=10,int b=10);
void fun(int a,int b)
{
	xxx;
}
可以
void fun(int a,int b);
void fun (int a=10,int b=10)
{
	xxx;
}
//声明函数与定义函数只有其中之一可以去初始化,不可以都去初始化
void fun (int a=10,int b=10);
void fun (int a=10,int b=10)
{
	xxx;
}


2-1函数的占位参数


就是你一定要给出一定的数据去占位置
好比你去图书馆,你可以不去图书馆,但是你要拿一本书去图书馆放着

其实也没什么
void fun(int a, int , double = 20)
{
	xxx;
}
调用
fun(10,20,30);
这个,已知10,但20 ,30不一定用得上,但是必须要写上去占位置

__
###3-1函数的重(chong)载(比较重要吧)吧


  1. 作用域
    还比都在全局区域
  2. 函数名称与类型一样same
  3. 函数的参数类型/参数个数/参数的顺序
    代码示意
void fun();//1
void fun(int a);//2
void fun (double a);//3
void fun (int a, double b);//4
void fun (double a,int b);//5
调用的话
fun()//调用第1个
fun(0)//调用第2个
fun(2.14)//调用第3个
fun(2,3.14)//调用第4个
fun(3.14,2)//调用第5给

3-2函数的重(chong)载的注意事项


//可读可写的选择
void fun ( int & x)
void fun ( const int & x)
fun(10);
	上面你把常量10给传递进去,10是只读的常量,因此他会调用下面那个
void fun(int a=10,b=20)
void fun(int a=10)
fun(20);
	//这里存在一个歧义
    //20上面下面都可以用
    //对于于是要fun(10,20)才会调用上面面那个
    

1-1导入部分
  1. 什么叫类?
    有点像结构体,这个类就是你创建的类型
  2. 什么叫成员?
    成员就是你类里面的东西,保活成员属性+成员方法/行为
  3. 什么叫成员属性?
    好比一个人,它的身高,体重就是属性
  4. 什么叫成员方法/行为?
    好比你给一个人下命令,它动了,下命令的具体化就是一行为或者方法
  5. 什么叫实例化?
    好比你赋予一个人身高1.8米,体重80Kg,让他去参加国家篮球队打篮球,
    这就叫实例化,其中身高1.8米,体重80Kg,叫成员属性初始哈,让他去参加国家篮球队打篮球,叫行为初始化
    为什么说,有点结构体
  6. 最后有个分号;
#include
#include
using namespace std;

struct p
{
	int age1;
	int age2;
	void set_a(int a)
	{
		age1=a;
	}
		
	void set_b(int b)
	{
		age2=b;
	}
		
	int get_age1()
	{
		return age1;
	}
	int get_age2()
	{
		return age2;
	}
	int sum()
	{
		return age1+age2;
	}
};

int main()
{
	
	p x;
	x.set_a(10);
	x.set_b(20);
	cout<<"x.get_age1()+x.get_age2()="< 

于是你往后面看的话,其实发现于类有感觉差不多,后面也会说结构体与类的区别,也就是一个权限的区别


2-1访问权限

什么叫类外面/类里面?

class xx(类名)
{
  ...
};
上面就是类的范围,也就是class的大括号囊括的部分

代码示意

#include
#include
using namespace std;


class student//定义了一个类,这就才生了一个范围
{
//类头
public:

	string Name;
	int ID;
	void show()
	{
		cout<<"name "< 

3-1struct与class的区别

是作用域的区别
然后的话,就应该没啥区别了

4-1成员属性设置为私有

其实这里面也没什么
就是一个访问的权限问题
也就是你不可直接访问,要绕一下才可以访问
可能这就有点像私有了吧
代码示意


#include
#include
using namespace std;

class Lover
{

public:
	
	//只可写
	void re_set(string name ,int age)
	{
		Name=name;
		Age=age;
	}
	//只可读
	void show()
	{
		cout<<"name: "< 
 

小插曲

类在声明的时候不可以初始化
同样的,结构体在声明的时候也不可以初始化

private:
	string Name="dqx";
	int Age=100;

上面就是错误的


做了一个实践的代码
这代码的话,值得注意的是

  1. 嵌套的类,一个类里面还有类.
c.get_center().get_x()
  1. 然后就是为什么会有类里的初始化与调用
    你看
c.get_center().get_x()

这里的get就是`一个调用,如果你不去

int get_x()
{
 return X;
}

请问你怎么流畅的获取,否者的话会比较麻烦

然后有兴趣的话,就看看下面的代码吧....

#include
#include
using namespace std;


class point//随机点位的设计
{
public:
	void set_x(int x)//设置x
	{
		X=x;
	}
	int get_x()//返回x
	{
		return X;
	}
	void set_y(int y)//设置y
	{
		Y=y;
	}
	int get_y()//返回y
	{
		return Y;
	}	
private:
	int X;
	int Y;
};

class circle//本圆的半径与圆心
{

public:
	void set_r(int r)//设置半径
	{
		R=r;
	}
	int get_r()//设置半径
	{
		return R;
	}

	void set_center(point center)//设置圆心
	{
		Center=center;
	}
	point get_center()//返回圆心//让另外一个类作为这个类的成员
	{
		return Center;
	}
	
private:
	int R;
	point Center;
	
};
void is_in_circle(circle &c,point &p)
{
	//计算2点距离的平方
	int distance=
		(c.get_center().get_x()-p.get_x())*(c.get_center().get_x()-p.get_x())+
		(c.get_center().get_y()-p.get_y()*c.get_center().get_y()-p.get_y());
	//计算半径的平方
	int len_r=c.get_r()*c.get_r();

	//判断
	if(distance==len_r)
		cout<<"on the circle"<len_r)
		cout<<"out of circle"< 

1-2-对象的初始化与清理
1-2-1-导入 构造函数

析构函数

他不会去返回什么,就只是执行里面的东西罢了
代码示意
上面也就是说
构造函数可以重载
析构函数不可以重载


#include
#include
using namespace std;

//对象的初始化与清理
//1.构造函数,进行初始化 *** 作
class person
{
public:
	//1.构造函数
	//没有返回值,不写void
	//函数名 与类的名称相同
	//构造函数可以有参数,可以发生重载
	//创建对象的时候,构造函数会主动调用,而其只会调用一次
	person()
	{
		cout<<"构造函数"< 

1-2-2构造函数的分类以及调用

大致框架
#include
#include
using namespace std;

class person
{
public:
	person()
	{
		cout<<"I am the 构造函数"< 

无参数构造
#include
#include
using namespace std;
//类的声明
class person
{
public:
	person()
	{
		cout<<"默认的构造函数,被调用了,人为修改了这里"< 

有参数构造
#include
using namespace std;
#include
//类的声明
class person
{
public:
	person(int x)
	{
		Age=x;
		cout<<"有参数构造,我被调用了"<

普通构造

只要不是拷贝就是普通构造
上面得构造都是普通构造

拷贝构造

注意拷贝时要用const不可修改,并且以引用得形式访问

person(const person &BUff );
person p2(p1);

#include
#include
using namespace std;
class person
{
public:
	person(int x)
	{
		age=x;
		cout<<"我是有参数构造,被调用了"< 

调用方式-括号法
#include
#include
using namespace std;

class person
{
public:
	person()
	{
		cout<<"我是构造函数,被明摆着被调用了"< 

调用方式-显示法

person nothing =person(20);

#include
#include
using namespace std;

class person
{
public:
	person(int x)
	{
		age=x;
		cout<<"我是构造函数,被明摆着被调用了"< 
调用的隐士法 
#include
#include
using namespace std;

class person
{
public:
	person(int x)
	{
		age=x;
		cout<<"构造函数"< 

1-2-3-拷贝构造函数调用的时机


上面的情况简单的来说

  1. 你主动去拷贝,当然会有拷贝函数的调用
  2. 当你把实参作为参数传递给一个函数,这里会涉及拷贝函数
  3. 你把函数实参作为结果返回return给另外一个调用者,会涉及拷贝
#include
#include
using namespace std;

class person
{
public:
	person()
	{
		cout<<"默认的打开,因为你在创建一个对象"<n");
	fun1();
	printf("n以函数的方式给函数参数传值-->n");
	person p;//为什么析构一次呢???
	fun2(p);
	printf("n以值的方式返回给局部对象-->n");
	fun3();
	cout<<"end"< 

到底什么时候去调用函数值?


构造函数的调用规则


怎么理解这3句话?
1.如果一个函数你一个都不去人为的构造,系统就全部用它的那3个函数
2. 如果你自己构造了有参构造函数系统就只会提供拷贝函数与析构函数
3. 如果你自己构造了拷贝函数,系统就只会提供析构函数,其它声明有参构造,无参构造,它都不会去提供
所以你如果你构造了一个函数,那么意味着其它的函数很大可能你都要构造

#include
#include
using namespace std;

class person
{
public:
	int age;
};
void fun()
{
	person dqx;
}
int main()
{
	fun();
	system("pause");
}
//系统自己用默默用它自己的3个函数

自己构造有参函数

#include
#include
using namespace std;

class person
{
public:
	person(int x)
	{
		age=x;
		cout<<"我是有参数的构造函数,被明摆着被调用了"< 

自己构造拷贝函数

#include
#include
using namespace std;

class person
{
public:
	person(int x)
	{
		age=x;
		cout<<"我是有参数的构造函数,被明摆着被调用了"< 
深度拷贝与浅拷贝 

所谓的浅拷贝就是原封不动,一模一样的拷贝过去

  • 它的问题在于不考虑细节

深拷贝会做一定的手脚

  • 它的好处在于顾及到了细节

到底是什么细节?
看后面吧

浅拷贝
#include
#include
using namespace std;

class person
{
public:
	person(int x,string name)
	{
		Age=x;
		Name = new string (name);//我们如何去手动的释放
		cout<<"我在这里开辟了一个堆区"< 

引发异常

为什么会出现这个情况
其实析构函数的一个大用处在于

	~person()//它的关键用处在于堆区数据的释放
	{
		delete Name;//如果本身为空,就不能再delete
		cout<<"我是析构函数,被明摆着调用了"< 

手动释放堆区,本来堆区的一些数据就是要程序员手动去释放的,于是这个析构函数就为程序员提供了一种方式
但是,这里开辟了2个对象,一个原样本,一个复制样本,因为是复制过去的,所以他们指向堆区的数据也都一样

Name=BUff.Name;//这里就是一五一十的拷贝,直接把指向堆区的地址给拷贝过去,他们就指向了同一片堆区

那么的话,delete Name的析构函数会指向2次,第一次把指向堆区的数据给delete,于是又来一个析构函数去delete堆区的数据,此刻原来堆区的数据已经被delete,那么再去delete就会引发异常
问题解决?

深度拷贝

(也就是不要一五一十的拷贝,而是在里面添加几笔)
让拷贝时,数据不是指向堆区同一片的数据,而是拷贝到不同的区域
接下来上代码

#include
#include
using namespace std;

class person
{
public:
	person(int x,string name)
	{
		Age=x;
		Name = new string (name);//我们如何去手动的释放
		cout<<"我在这里开辟了一个堆区"< 

上面可以看见,他不是把地址给拷贝了过去,而是重新开辟了一个堆的数据

Name=new string(*BUff.Name);
初始化列表给类赋初值

格式

person xxx(int a,int b):A(a),B(b)
{
}

代码

#include
#include
using namespace std;
class person
{
public:
	person(int age,string name) :Age(age),Name(name)
	{

	}
	
	int Age;
	string Name;
};
void fun()
{
	person dqx(20,"dqx");
	cout< 
类对象作为类成员(嵌套式类) 

还是类比函数一样…或者for的嵌套循环,.
这个例子结合了一下上面一节的初始化列表
上代码

#include
#include
using namespace std;


class PC
{
public:
	PC(string pc_name,string users_name,int pc_price):PC_Name(pc_name),Users_Name(users_name),PC_Price(pc_price)
	{

	}
	string PC_Name;
	string Users_Name;
	int PC_Price;
};
class person
{
public:
	person(string name,string users_name,string pc_name,int price):Name(name),pc(pc_name,users_name,price)
	{

	}
	string Name;
	PC pc;
};
void fun()
{
	person dqx("dqx","XiaoMi","Re_lover",5000);
	cout< 

犯的小错误
class{}没有加分号

静态成员

它的一个特性是共享吧…

  1. /静态的函数只能调用静态变量,为什么?
    //因为静态变量与静态函数不会属于任何一个对象,
    那么如果其中出现了一个非静态变量,而非静态变量又
    必须要属于一个对象,静态与非静态混合在一起,共享与必须属于一个对象混合在一起,
    就会发生矛盾,因为静态函数不知道
    那个非静态的变量到底会属于谁谁,我们的静态函数也不知道
    /
  2. //静态成员大家都可以用,所以不会属于任何一个对象
  3. //非静态的变量,只用创建一个对象后才能去使用
  4. //静态函数与变量也会又私有权限
  5. //静态变量的用法…类里声明…类外初始化
int lei::age1=0;
int lei::age2=0;
int lei::age4=0;
	代码示意图
#include
#include
using namespace std;


class lei
{
public:
	int age;
	static void fun1()//程序共享一个静态函数
	{
		age1=18;
		age2=20;
		//age3=12;
		
	}
	//静态成员大家都可以用,所以不会属于任何一个对象
	static int age1;
	static int age2;
	//非静态的变量,只用创建一个对象后才能去使用
	int age3;
private://静态函数与变量也会又私有权限
	static void private_fun()
	{
		cout<<"你抓不到我的"< 
成员变量与成员函数分开存储 

在一个类里面
成员变量与成员函数的存储位置不一样
同时静态的成员又本来不属于类
所以计算sizeif()的时候,其实呢是算的非静态成员变量的大小
当类空的时候…
为了证明那个空类还存在过,计算机会分配一个字节给他意思意思一下,所以空类的大小是1Byte

代码示意图

#include
#include
using namespace std;

class lei1{};
class lei2
{
public:
	int age1;
	static int age2;
	static void fun1(){}
	void fun2(){}
	int fun3(){}
};
int lei2::age2=0;
void fun2()
{
	cout<<"空类占多少字节 ? "< 

don’t look at me


C++一些高级的应用 暂停 pause
#include
...
system("pause");
清屏 cls
#include
...
system("cls");

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5692684.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存