c++拷贝构造&友元函数

前言

首先感谢您能打开我的文章,希望您能看完,有不对或者可以改进的地方,留言在下面,再次感谢。自己把学习过程的一些方法和笔记,以及针对出现的各种错误解决方法共享在博客里面。不断地在提升自己,觉得这个平台挺好的,可以记录在学习过程中点点滴滴。虽然自己很菜,但我还是会一如既往的分享,努力变的优秀,会一直努力不断地改进。希望对您有帮助。

一 拷贝构造

拷贝构造 是一种特殊的构造函数 用自身这种类型来构造自身
用户未定义拷贝构造,系统默认提供一个隐式的拷贝构造,它会将已存在于对象中的数据成员逐个的拷贝到新创建的对象中(浅拷贝)
拷贝构造:类名 (const 类名& 引用名);
拷贝构造:用自身这种类型来构造自身,是一种特殊的构造

class A
{
public:
  A(){}//无参构造(默认构造)
  A(int a){}//有参构造
  A(const A &CA){}//拷贝构造  在这个里面把传进来的对象一一赋值给新的对象
};

1.1浅拷贝

没有给新的指针申请内存来存内容,新的对象里面的指针和传进的指针都是指向同一块内存
在这里插入图片描述
这样操作之后,在最后释放内存的时候,对象A的指针的内存被释放之后,就相当于对象B的也被释放了,对象B的指针没有内存可以释放。如果在是使用中,释放了对象A的指针内存,那么同样对象B的也被释放了,那么对象B里面的指针就没有了内存

1.2深拷贝

给新的指针申请内存来存内存,这样就不会出现浅拷贝所出现的问题了
在这里插入图片描述
这样每个对象使用的都是自己的内存,对象A被释放,那么这里并不会影响到对象B,这里的深拷贝是需要自己将已存在于对象中的数据成员逐个的拷贝到新创建的对象中,指针就申请内存来赋值

1.3示例:

class A
{
public:
 A(int x, int y, int z) :a(x), b(y),c(z){}//第一种初始化列表的使用,和构造之直接赋值一样效果
 /*A(int x, int y, int z)
 {
 a = x;
 b = y;
 z = y;
 }*/
  int a, b, c;
};

1,如果类里面有const成员变量必须要用初始化列表

class A
{
public:
 A(int x, int y) :a(x), b(y){}
 /*A(int x, int y)
 {
 a = x;
 b = y;
 }*///这种方式赋值是不可以给const成员变量赋值的
 int a;
 const int b;
};

,2,成员变量或基类未声明默认构造函数

class A
{
public:
 A(int x, int y, int z) :a(x), b(y), c(z){}
 int a;
 int b;
 int c;
};
class B
{
public:
 B(int x,int y,int z):a(x,y,z){}//初始化列表
 A a;//A类作为B类的成员
};

3,数据成员按照他们在类中的声明顺序来初始化,而不是按照在初始化列表中出现的顺序。

class A
{
public:
 A(int x, int y, int z) :a(x), b(y), c(z){ cout << "A" << endl; }
 int a;
 int b;
 int c;
};
class B
{
public:
 B(int x, int y, int z) :aa(x, y, z), a(x, y, z){ cout << "B" << endl; }//初始化列表
 A a;
 A aa;
};
class C
{
public:
 C():a(0,0,0),b(0,0,0){}
 
 B b;
 A a;
};

4,拷贝构造

class student
{
	char *name;
public:
	student(char *name)
	{
		cout << "带参构造" << endl;
		this->name = new char[strlen(name) + 1];
		strcpy(this->name, name);
	}
	//拷贝构造:类名(const 类名& 引用名);
	student(const student& stu)
	{
		cout << "拷贝构造" << endl;
		this->name = new char[strlen(stu.name) + 1];
		strcpy(this->name, stu.name);
		//深拷贝其实也就是一个申请内存的操作,逐一拷贝对象里面的所有数据
	}
	void getName()
	{
		cout << name << endl;
	}
	~student()
	{
		cout << "析构" << endl;
		if (name != NULL)
		{
			delete[]name;
			name = NULL;
		}
	}
};

二 浅拷贝和深拷贝的区别

深拷贝和浅拷贝最根本的区别在于:
是否真正获取一个对象的复制实体,而不是引用。
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝(修改堆内存中的不同的值)

三 类中静态成员

1、类中的静态数据成员,所有对象都共享这一个成员,这个静态数据成员只有一份存在于内存中
2、类中的静态函数成员,因为他不属于对象,所以在这个函数中只能操作类的静态数据,静态函数
3、类中的静态数据成员,不管共有私有都是在类外进行初始化
4、类中静态数据成员初始化,静态数据成员不属于对象,无法通过对象的操作进行初始化,它是属于类的
5、静态数据成员,可以被重新赋值,可以被普通函数访问

class A
{
public:
 int a;
 static int m;//类中的静态数据成员,所有的对象都共享这一个成员,这个静态数据成员只有一份存在于内存中
public:
 A()
 {
  m++;
  //m = 123;不能在类内初始化  必须在类外初始化  类型 类名::静态成员=初始化值
 }
 ~A()
 {
  m--;
 }
 void setM(int i){ m = i; }//但是可以被重新赋值
 int getM(){ return m; }//类中的静态数据成员可以被普通函数访问
 static int fun()//静态成员函数也不属于任何对象,只属于类
 {
  //a += 10;因为它不属于对象,所以在这个函数中只能操作类的静态数据和静态函数
  m += 10;
  int a=12;
  a++;
  cout << m << endl;
  return m;
 }
};
int A::m = 0;

四 类中常量成员

1、类中常量成员分为两种:常量数据成员和常量函数成员
2、类中的常量数据成员,必须要使用初始化列表的方式进行初始化 <const int a;>
3、类中的常量函数成员,这个函数不能对自身变量进行修改,这个是常量的特性 <void fun() const {}>
(1)受语法限定,只要是this指针所指向的所有数据,在这个函数里面都不可以改
(2)初始化之外的形参数据,临时变量,不属于this指针的数据都可以改
(3)根据语法,可以帮助我们来限定去修改自身的数据

4、对象被说明说常量对象时,这个对象只能调用常量数据成员函数

(1)这个对象里面的所有数据都是不可以被修改的

(2)可以对象里面的静态成员,因为静态成员不属于对象

class A
{
public:
 int a;
 const char sex;//类中常量数据成员
 static int b;

 A(char s) :sex(s)
 {
  a=18;
 }
 void fun1()const//类的常量函数成员,const必须写再函数的后面
 {
  //a += 10;不能对自身变量进行修改,这个是常量的特性
  //受语法限定,只要是this指针所指向的所有数据,在这个函数里面都不可以改
  //初始之外的形参数据,临时变量,不属于this指针指的数据,都可以改
  int n = 10;
  n++;
 }
 //1、根据语法可以帮助我们来限定在函数去修改自身的数据
 //2、当对象被说明为常量对象时,只能够调用常量成员函数
 static void fun2(int a)
 {
  a = 12;
 }
 void fun3(){}
};
int A::b = 0;

五 类中的友元成员

1、友元分为两种:友元函数,友元类
2、友元函数:是一个函数,这个函数不是对象的成员,对象是点不出来的,不管在哪里定义,但是这个函数有权调用类的所有的私有成员以及保护成员 声明一个友元函数:把函数的声明写在类中,无论私有共有保护,然后在前面加上一个friend即可
3、友元成员:类B为类A的友元类
B类和A类是两个类,友元类B必须在内外说明,B类所有成员函数都有权访问A类的所有成员

class B;//声明类
class A
{
 int a;
public:
 friend class B;
 friend void fun(A *srca);//友元函数的声明,这个函数不属于类
 int geta()
 {
  return a;
 }
};
class B
{
public:
 void fun(A&a)
 {
  a.a = 123;
 }
};
void fun(A &srca)
{
 srca.a = 10;
}

4、友元的三个特点
(1)单方向:B是A的朋友,B可以访问A的数据,A不可以访问B的数据
(2)不传递:A是B的朋友,B是C的朋友,A和C没有朋友关系
(3)不继承:A是B的朋友,B是C的父亲,A和C没有关系

六 const和static成员

6.1const

1,,const 数据成员
构造函数写法:必须使用初始化参数列表
初始化后:不能修改(不能再次赋值,不能自增)
2,const 成员函数,普通函数() const
const 成员函数不能修改基本数据成员
3,cosnt 对象 常对象只能调用常成员函数

class Cstu
{
public:
	//常数据成员 ---它是不能被修改
	const int a;
	const int b;
	//初始化构造函数的写法----必须要使用初始化参数列表
	//初始化完后不能被修改
	Cstu(int x, int y) :a(x), b(y){}
	void show()
	{
		//不能自增
		//a++;
		//只能初始化一次
		//b = 12;
		cout << "a=" << a << endl;
		cout << "b=" << b << endl;
	}
};

6.2static

1,static 数据成员
公有性 所有对象公有的
内存分配,程序运行时分配好
使用前必须被初始化
静态数据成员只能被初始化一次
初始化方式 类型 类名::变量名=值;
2, static 成员函数
不属于对象,访问不需要对象
静态成员函数可以访问非静态数据成员

class A
{
public:
	static void fun(A a);
private :
	int x;
};
//在类外实现不需要加static
void A::fun(A a)
{
	//非静态成员的使用必须指定
	//cout << x << endl;
	cout << a.x << endl;
}

七 总结

如果类中又动态申请内存,必须要重写拷贝构造,来做深拷贝
拷贝构造的使用情况:
1、用一个类对象去初始化该类的另一个对象时
2、如果函数的形参是类的对象,调用函数时进行实参传递时,调用拷贝构造
3、如果一个函数的返回值是类对象,函数调用返回时
类里的数据成员初始化:
1、成员列表的初始化在一些特殊的情况下,数据成员的初始化只能用初始化列表,不能直接赋值
2、必须写在构造函数的定义后面,用:表示,如果用对各成员用逗号隔开
3、构造函数能对数据进行的操作,初始化列表也都可以

猜你喜欢

转载自blog.csdn.net/qq_45893999/article/details/106456811