C++ C3 -深拷贝与浅拷贝-new和delete-初始化成员列表-构造中调用构造-静态static-this指针-友元-const

本文深入探讨了C++中的深拷贝与浅拷贝概念,强调了默认拷贝构造函数可能导致的问题及其解决办法。接着详细介绍了new和delete操作符在动态内存分配中的应用,以及与malloc/free的区别。此外,文章还讲解了初始化成员列表的重要性,特别是在处理带参数构造函数、const成员和继承时的角色。同时,提到了构造函数中调用构造的潜在风险。最后,阐述了静态成员变量和静态成员函数的特点,以及this指针、友元和const在类设计中的作用。

一、深拷贝与浅拷贝
浅拷贝:没有自己写拷贝构造函数,导致程序出现要拷贝的时候调用了默认的拷贝构造函数,在普通的变量上不会有什么影响但是对于指针而言,会造成野指针现象。
默认拷贝构造函数,只是单一的复制,在普通变量上就是把数值复制过来,这不会有什么问题,但是涉及到指针,默认拷贝构造函数做的也是仅仅将指针地址复制过来。这就出现问题了,我们希望的是:一开始有一个对象A,然后对象B要来做拷贝。B对象应该重新开辟一块空间存储对象A的那个指针所指向地址中存的数据。但是默认拷贝构造必不会重新开辟空间,仅仅是将B的指针指向A的地址。这就导致在析构对象时,A对象的指针地址已经回收了,但是B对象的指针仍然指向那块空间。在B对象析构时,就会出错,因为那块空间已经没了,但是你还要去释放,就导致段错误。这一错误是在运行阶段出现的,编译时并不会报错!
在这里插入图片描述

class Student{
private:
    char* name;
    int id;
public:
    Student(int id,char* name);
    ~Student();
};

Student::Student(int id,char* name){
    this->id=id;
    this->name=(char*)malloc(sizeof(char)*10);
    strcpy(this->name,name);

}
Student::~Student(){
    if(name!=NULL){
        free(name);
        name=NULL;
    }
}
int main(){
    char a[10]="hello";
    Student S1(10,a);
    Student S2=S1;//程序结束以后就会出运行错误
    return 0;
} 

深拷贝
手动写拷贝构造函数,给指针分配空间

Student::Student(const Student& obj)
{
		m_age = obj.m_age;
		m_name = (char*)malloc(sizeof(char)*20);
		strcpy(m_name,obj.m_name);
}

二、new和delete
new 形式: 1.普通
指针变量 = new 类型
指针变量 = new 类型(常量)//赋初值 ptr = new int (10) *ptr=10
2.数组
指针变量 = new 类型[表达式]
3.对象
类名 指针对象 = new 类名
会调用构造函数
delete形式:
1.普通
delete 指针变量
2.数组
delete[] 指针变量(如果不加[ ]会造成内存泄漏
3.对象
delete 对象;
会调用析构函数;
1.普通
1.int *n_pa=new int;
*n_pa=10;
delete n_pa;
2.int *n_pa = new int(10);
delete n_pa;
2.数组
int *n_pa = new int[10];
delete[] n_pa;
3.对象
test *ptr = new test;
delete ptr;
malloc/free 和 new/delete有什么区别
malloc/free不是C语言的语法,他们时由标准库提供的函数;
new/delete是C++的操作符,是一种语法;
对于非内部数据类型的对象而言,对象要在消亡之前执行析构函数
由于malloc和free是库函数不是运算符,不在编译器控制权限之内
不能把执行构造/析构函数的任务加在malloc/free身上,因此只能用new/delete
三、初始化成员列表
必须用初始化成员列表的情况:
case1:如果一个对象时某一个类的成员变量(组合对象)
并且这个成员中有带参数的构造函数,没有默认构造函数
case2:如果成员变量里面有const修饰的变量
3.初始化基类中继承过来的成员变量(用的是类名)
初始化列表的构造/析构顺序:
1.基类
2.当类中有成员变量是其他类的对象时,首先调用这个成员变量的构造函数,然后调用自身构造函数
3.调用顺序与生命顺序相同,与初始化列表顺序无关
4.析构函数的调用顺序与构造函数正好相反

class test1
{
private:
	int m_a;
	int m_b;
public:
	test1(int a,int b)
	{
		m_a = a;
    m_b = b;
		cout << "test1构造" << endl;
	}
	~test1(){cout << "test1析构" << endl;}
void printT1()
{
		cout << m_a << "," << m_b << endl;
	}
};
class test3
{
private:
	int m_a;
	int m_b;
public:
	test3(int a,int b):m_a(a),m_b(b)
	{
		/*m_a = a;
		m_b = b;*/
		cout << "test3构造" << endl;
	}
	~test3(){cout << "test3析构" << endl;}
	void printT1()
	{
		cout << m_a << "," << m_b << endl;
	}
};
class test2
{
private:
	int m_c;
	int m_d;
	test1 t1;//这两行换位置就是生命顺序,构造函数调用会变;
	test3 t2;
	const int m_q;
public:
	test2(int c,int d,int x,int y,int w,int z):t1(x,y),t2(w,z),m_q(10)
	{
		m_d = d;
		m_c = c;
		cout << "test2构造" << endl;
	}
	~test2(){cout << "test2析构" << endl;}
	void printT2()
	{
		cout << m_c << "," << m_d << endl;
   		 t1.printT1();
		t2.printT1();
		cout << "const m_q:" << m_q << endl;
	}

};
void func()
{	test2 t1(1,2,3,4,5,6);
	t1.printT2();
}
int main()
{
	func();
	return 0;
}
运行结果为:
test1构造
test3构造
test2构造
1,2
3,4
5,6
const m_q:10
test2析构
test3析构
test1析构

四、构造中调用构造
Test(int a,int b,int c)
{
m_a = a;
m_b = b;
m_c = c;
}
Test(int a,int b)
{
m_a = a;
m_b = b;
Test(a,b,10); //匿名对象(生命周期分号就结束)
}
构造中调用构造是很危险的操作:因为匿名对象的生命很短,执行完以后就结束了,
Test(a,b,10)在外面并不会对c有什么影象;要避免使用
五、静态static
静态成员变量:
1. !!必须在类外初始化
类型 类名::变量名字=值;
2.对于公有的静态成员变量在访问时
1.可以用任何对象直接访问
2.类名::变量名字
最大的特点:“共享机制”
无论创建了多少个对象,都只创建一个静态变量的副本
这些对象都共享这个static静态成员变量
静态!全局!变量和全局变量的区别:
作用域不同:静态全局变量仅在当前源文件有用;
全局变量在整个程序都可用(一个程序可能有多个源文件)
静态成员函数
静态成员函数
1.静态成员函数只能使用静态成员变量,你不可以使用普通变量(可以使用普通自定义函数)
2.形式: static 修饰函数
3.静态成员在创建对象之前就已经存在(独立于对象之上)
所有对象都共享这个静态成员函数
访问方式:
①可以用任何一个对象去访问
②类名::公有静态成员函数
六、this指针
是一个指向当前被操作的对象的特殊指针(大部分情况是隐式调用,也可以显式调用)
七、友元
关键字:friend
分为三大类:友元函数,友元成员函数,友元类
一、友元函数
声明:
①friend int get_area(); //没有接口
②friend int get_area2(类名& s); //有接口
注意:友元函数本质上破坏了类的封装性,应该谨慎使用
class CBox
//{
//private:
// int i;
// int j;
//public:
// CBox(int i,int j)
// {
// this->i = i;
// this->j = j;
// }
// ~CBox(){}
// friend int get_area();
// friend int get_area2(CBox& s);
//
//};
//
//int get_area()
//{
// CBox c1(10,20);
// return c1.i * c1.j;
//}
//int get_area2(CBox& s)
{
return s.i*s.j;
}
二、友元成员函数
在类A2中声明友元关系,表示A2愿意将自己和外部分享
注意:要在类的前面做好其他类的声明,不然会报没有声明的错

!!	class A2
		class A1
		{
		private:
			int a,b;
		public:
			A1(int a,int b):a(a),b(b){};
			~A1(){};
			int get_A(){ return a;}
			void change(A2& a);
		};
		class A2
		{
		private:
				int c,d;
		public:
				A2(int c,int d):c(c),d(d){};
				~A2(){};
				int get_C(){return c};
				friend void A1::change(A2& a);	
		};
		void A1::change(A2& a)
		{
				cout<<a.c<<a.d<<endl;
		}
		int main()
		{
			A1 a1(1,1);
			A2 a2(2.2);
			a1.change(a2);
			return 0;
		}
	**三、友元类**
		在A中声明了friend class B,那么B是A的友元,
		B类中可以控制A类的状态。
		(B类中所有的成员函数都是A类的友元函数)

八、const
const成员变量/const对象/const成员函数
const对象
!!任何修改const对象中的成员变量的行为都会报错
如何声明?
A const a1(1,2);
const A a1(1,2);
const对象只能访问对象中的const成员函数;
const对象可以调用但是不可以修改公有成员数据;
const成员函数
const int get_a(); 仅仅表示该函数返回一个常量
int get_a() const; const成员函数声明
1.const对象只能访问对象中的const成员;
2.普通对象优先调用非const成员变量,如果没有,那就调用const成员变量

class Sample
{
private:
	int a;
public:
	int b;
	Sample(int x,int y):a(x),b(y)
	{
	}
	~Sample(){}
	void printS() const
	{
		cout << a << "," << b << endl;
		cout << "const" << endl;
	}
	void printS2()
	{
		cout << a << "," << b << endl;
		cout << "putong" << endl;
	}
};

int main()
{
	Sample const S1(10,20);
	cout << S1.b << endl;//const对象可以调用但是不可以修改公有成员数据
	S1.printS();
	//S1.printS2();   const对象只能访问对象中的const成员

	Sample S2(10,20);
	S2.printS2();
	S2.printS();

	system("pause");
	return 0;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值