一、深拷贝与浅拷贝
浅拷贝:没有自己写拷贝构造函数,导致程序出现要拷贝的时候调用了默认的拷贝构造函数,在普通的变量上不会有什么影响但是对于指针而言,会造成野指针现象。
默认拷贝构造函数,只是单一的复制,在普通变量上就是把数值复制过来,这不会有什么问题,但是涉及到指针,默认拷贝构造函数做的也是仅仅将指针地址复制过来。这就出现问题了,我们希望的是:一开始有一个对象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;
本文深入探讨了C++中的深拷贝与浅拷贝概念,强调了默认拷贝构造函数可能导致的问题及其解决办法。接着详细介绍了new和delete操作符在动态内存分配中的应用,以及与malloc/free的区别。此外,文章还讲解了初始化成员列表的重要性,特别是在处理带参数构造函数、const成员和继承时的角色。同时,提到了构造函数中调用构造的潜在风险。最后,阐述了静态成员变量和静态成员函数的特点,以及this指针、友元和const在类设计中的作用。
419

被折叠的 条评论
为什么被折叠?



