Day40、this指针和常函数、析构函数、对象的创建和销毁过程、拷贝构造和拷贝赋值(深拷贝、浅拷贝!)
来源:互联网 发布:侏罗纪世界4 知乎 编辑:程序博客网 时间:2024/05/22 23:39
一、 this和常函数
1、 this 指针
1) 类中的构造函数和成员函数都隐藏一个该类类型的指针参数,参数名为this。
2) 对于普通的成员函数,this指针就是指向调用该函数的对象
3) 对于构造函数,this指针指向正在被构造的对象
举例:
1#include<iostream>
2using namespace std;
3class User{
4public:
5 User(const string&name,int age):
6 m_name(name),m_age(age){
7 cout<<"A::A()"<<this<<endl;
8 }
9 //void print(User* this){
10 void print(void){
11 cout<<m_name<<','<<m_age<<endl;
12 cout<<this->m_name<<','<<this->m_age<<endl;
13 //this指针指向调用对象
14 cout<<"this="<<this<<endl;
15 }
16private:
17 string m_name;
18 int m_age;
19};
20int main(void){
21 User user1("张飞",25);
22 cout<<"&user1:"<<&user1<<endl;
23 user1.print();//print(&user1)
24 return 0;
25 }
2、 必须使用this指针的地方
1) 区分作用域
1#include<iostream>
2using namespace std;
3class User{
4public:
5 //构造形参和成员变量一样的名字,通过this区分
6 User(const string&name,int age){
7 this->name=name;
8 this->age=age;
9 }
10 void print(void){
11 cout<<name<<","<<age<<endl;
12 }
13private:
14 string name;
15 int age;
16};
17int main(){
18 User user("tangzihao",24);
19 user.print();
20 return 0;
21 }
2) 从成员函数中返回对象自身
1 #include<iostream>
2using namespace std;
3class counter{
4public:
5 counter(intdata=0):m_data(data){}
6 counter&add(void/*counter* this*/){
7 ++m_data;
8 return *this;//返回自引用
9 }
13 int m_data;
14};
15int main(){
16 counter cn;
17 cn.add().add().add();//add(&cn)
18 cout<<cn.m_data<<endl;//3
21 return 0;
22 }
3) 从类的内部销毁该对象自身-----对象自毁
1#include<iostream>
2using namespace std;
3class counter{
4public:
5 counter(intdata=0):m_data(data){}
6 counter&add(void/*counter* this*/){
7 ++m_data;
8 return *this;//返回自引用
9 }
10 void destory(void){
11 delete this;
12 }
13 int m_data;
14};
15int main(){
16 counter cn;
17 cn.add().add().add();//add(&cn)
18 cout<<cn.m_data<<endl;//3
19 counter* pcn=new counter;
20 pcn->destory();//destory(pcn)
21 return 0;
22 }
4)作为函数的实参,实现对象间数据交互(了解)
1#include<iostream>
2using namespace std;
3class student;//短视声明
4class teacher{
5public:
6 void educate(student*student);
7 void reply(const string&answer){
8 m_answer=answer;
9 }
10private:
11 string m_answer;//保存答案
12};
13class student{
14public:
15 void ask(const string& question,teacher* t){
16 cout<<"问题:"<<question<<endl;
17 t->reply("this指针指向调用对象的地址");
18 }
19};
20void teacher::educate(student* student){
21 student->ask("什么是this指针",this);
22 cout<<"学生回答"<<m_answer<<endl;
23}
24int main(void){
25 teacher tch;
26 student sdt;
27 tch.educate(&sdt);
28 return 0;
29 }
3、常函数
1)在一个普通的成员函数参数表后面加上const,这个成员函数称为常函数
返回类型 函数名(形参表)const {函数体}
2)常函数中的this指针是一个常指针,不能在常函数中修改成员变量的值
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(int data=0):m_data(data){}
6 /*如果定义一个成员函数,里面不需要修改成员变量,只是访问,那么应该声明为 常函数*/
7 void show(void)const{//constA* this
8 cout<<m_data/*++*/<<endl;// 不能修改成员变量,如果修改会报错
9 }
10private:
11 int m_data;
12};
13int main(void){
14 Aa(123456);
15 a.show();
16 a.show();
17 return 0;
18 }
123456
123456
3)mutable关键字:修饰的成员变量可以在常函数中修改
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(int data=0):m_data(data){}
6 void show(void)const{//constA* this
7 /* cout<<
8 const_cast<A*>(this)->m_data++<<endl;*/
9/*mutable修饰的成员变量在常函数中使用时会自动做常量转换,和上句意思一样*/
10 cout<<m_data++<<endl;
11 }
12private:
13 mutable int m_data;
14};
15int main(void){
16 Aa(123456);
17 a.show();
18 a.show();
19 return 0;
20 }
tarena@tarena-virtual-machine:~/day40$./a.out
123456
123457
4) 非常对象既可以调用非常函数,也可以调用常函数,但是常对象只能调用常函数,不能调用非常函数
1#include<iostream>
2using namespace std;
3class A{
4public:
5 void func1(void)const{//this-->const A*
6 cout<<"常函数"<<endl;
7 }
8 void func2(void){
9 cout<<"非常函数"<<endl;
10 }
11};
12int main(void){
13 Aa;
14 a.func1();//&a-->A
15 a.func2();
16 const A a2=a;
17 //a2是个常对象,常对象只能调用常函数,不能调用非常函数
18 a2.func1();//&a2-->const A*
19 //a2.func2();error 只读调用可读可写,扩大范围,不行
20 const A* pa=&a;//pa常指针
21 pa->func1();
22 //pa->func2();
23 const A& ra=a;//ra常引用
24 ra.func1();
25 //ra.fun2();
26 return 0;
27 }
tarena@tarena-virtual-machine:~/day40$./a.out
常函数
非常函数
常函数
常函数
常函数
5) 函数名形参表相同的成员函数,其常版本和非常版本可以构成有效的重载关系,常对象调用常版本,非常对象调用非常版本
1 #include<iostream>
2using namespace std;
3class A{
4public:
5 void func(void)const{//this-->const A*
6 cout<<"常版本"<<endl;
7 }
8 void func(void){//this-->A* //可以构成重载函数
9 cout<<"非常版本"<<endl;
10 }
11};
12int main(void){
13 Aa;
14 a.func();//调用非常版本
15 const A a2=a;
16 a2.func();
17 return 0;
18 }
tarena@tarena-virtual-machine:~/day40$./a.out
非常版本
常版本
二、 析构函数(Destructor)
(创建对象的时候调用构造函数)
1、 析构函数和构造函数形式类似,是类中特殊的成员函数
class 类名{
~类名(void){….}
}
1)函数名必须是:~类名
2)没有返回类型,也没有参数,所以不能被重载,一个类只能有一个析构函数。
3)主要负责清理对象生命周期中产生的动态资源
(如果用销毁new的成员变量比较麻烦,万一忘记,会造成内存泄露,析构函数自动释放new出来的)
1#include<iostream>
2using namespace std;
3class integer{
4public:
5 integer(intdata=0):m_data(new int(data)){}
6 ~integer(void){
7 cout<<"integer::~integer()"<<endl;
8 delete m_data;
9 m_data=NULL;
10 }
11 int get(void)const{
12 return *m_data;
13 }
14private:
15 int* m_data;
16};
17int main(void){
18 integer i(100);
19 cout<<i.get()<<endl;//100
20 return 0;
21 }
2、 当对象被销毁时,该对象的析构函数将自动被执行
(构造函数:当对象被创建时,该对象的构造函数将自动被调用)
1) 栈对象(局部变量)当其离开作用域时,其析构函数被作用域终止运算符“}”调用
2) 堆对象(new出来的)的析构函数被delete运算符调用
1#include<iostream>
2using namespace std;
3class A{
4 public:
5 ~A(void){
6 cout<<"A::A~()"<<endl;
7 }
8};
9int main(void){
10 {
11 A a;
12 cout<<"test1"<<endl;
13 A* pa=new A;
14 delete pa;// delete -->A::~A()
15 cout<<"test3"<<endl;
16 }// } -->A::~A() 对象的作用域结束时调用析构函数
17 cout<<"test2"<<endl;
18 return 0;
19 }
test1
A::A~()
test3
A::A~()
test2
3、 如果一个类没有显式定义析构函数,那么系统会为该类提供一个缺省析构函数
1)对基本类型的成员变量,什么都不做
2)对类类型的成员变量,调用相应类型的析构函数,析构成员子对象
1#include<iostream>
2using namespace std;
3class A{
4public:
5 A(void){
6 cout<<"A::A()"<<endl;
7 }
8 ~A(void){
9 cout<<"A::~A()"<<endl;
10 }
11};
12class B{
13public:
14 B(void){
15 cout<<"B::B()"<<endl;
16 }
17 ~B(void){
18 cout<<"B::~B()"<<endl;
19 }
20 Am_a;
21};
22int main(void){
23 Bb;
24 return 0;
25 }
tarena@tarena-virtual-machine:~/day40$./a.out
A::A()
B::B()
B::~B()
A::~A()
4、 对象的创建和销毁过程
1) 对象的创建
-à为对象分配内存空间
-à依次调用类类型成员变量的构造函数,构造成员子对象
à执行构造函数体代码
2) 对象的销毁
-à执行析构函数的代码、
-à调用成员子对象的析构函数
-à释放对象所占的内存
(以上代码)
三、 拷贝构造和拷贝赋值
A a1;
A a2(a1); // 拷贝构造
A a3;
a3=a1;// 拷贝赋值
1、浅拷贝和深拷贝
如果一个类包含指针形式的成员变量,缺省的拷贝构造函数只是赋值指针成员变量本身(拷贝地址),而没有赋值该指针所指向的内容,这种拷贝方式称为浅拷贝。
浅拷贝将导致不同对象间数据共享(多个对象指向同一块内存中的值),如果数据是在堆区,会在析构时引发“doublefree”异常,因此就必须自己定义一个支持复制指针所指向内容的拷贝构造函数,即深拷贝
1#include<iostream>
2using namespace std;
3class Integer{
4public:
5 Integer(intdata=0):m_data(new int(data)){} //缺省构造函数
6 /* 缺省拷贝构造函数,浅拷贝 error//(拷地址)
7 Integer(const Integer&that):
8 m_data(that.m_data){}*/
9 Integer(const Integer&that):
10 m_data(new int(*that.m_data)){}//深拷贝:(分配地址拷数据)
11 ~Integer(void){
12 delete m_data;
13 m_data=NULL;
14 }
15 int get(void)const{
16 return *m_data;
17 }
18private:
19 int* m_data;
20};
21int main(void){
22 Integer i1(100);
23 Integer i2(i1);//拷贝构造
24 cout<<i1.get()<<endl;//100
25 cout<<i2.get()<<endl;//100
26 return 0;
27 }
tarena@tarena-virtual-machine:~/day40$./a.out
100
100
2、 类的缺省拷贝赋值和缺省拷贝构造一样,都是浅拷贝,为了得到深拷贝的效果,必须自己定义一个支持深拷贝的拷贝赋值运算符函数。
1) 防止自赋值
2) 释放旧资源
3) 分配新资源
4) 赋值新内容
5) 返回自引用
拷贝构造和拷贝赋值代码:
#include<iostream>
using namespace std;
class Integer{
public:
Integer(int data=0):m_data(new int(data)){}
/*缺省拷贝构造函数,浅拷贝 error//(拷地址)
Integer(const Integer& that):
m_data(that.m_data){} */
Integer(const Integer& that):
m_data(new int(*that.m_data)){}//深拷贝:(分配地址拷数据)
/*编译器会自动添加一个拷贝赋值函数*/
/*缺省拷贝赋值一样也是浅拷贝*/
/*Integer& operator=(const Integer& that){
cout<<"缺省拷贝赋值运算符函数"<<endl;
m_data=that.m_data;
return *this;
}*/
/*缺省的浅拷贝会引发doublefree,所以自己定义深拷贝赋值*/
Integer& operator=(const Integer& that){//深拷贝
if(&that!=this){//地址比较 防止自赋值
delete m_data;//释放旧资源
//分配新资源
//拷贝新数据
m_data=new int(*that.m_data);
}
return *this;//返回自引用
}
~Integer(void){
delete m_data;
m_data=NULL;
}
int get(void)const{
return *m_data;
}
private:
int* m_data;
};
int main(void){
Integer i1(100);
Integer i2(i1);//拷贝构造
cout<<i1.get()<<endl; //100
cout<<i2.get()<<endl; //100
Integer i3;
i3=i2;//拷贝赋值:i3.operator=(i2);operator是左值
cout<<i3.get()<<endl; //100
return 0;
}
练习:实现String类 (实现库里的string类)
class String{
public:
//构造函数
//析构函数
//拷贝构造
//拷贝赋值
private:
char*m_str;
}
代码:重要!!!手写
#include<iostream>
#include<cstring>
using namespace std;
class String{
public:
//构造函数
String(const char* str=""):
m_str(strcpy(new char[strlen(str)+1],str)){}
//析构函数
~String(void){
delete[] m_str;
m_str=NULL;
}
//深拷贝构造
String(const String& that):
m_str(strcpy(new char[strlen(that.m_str)+1],that.m_str)){}
//深拷贝赋值
String& operator=(const String& that){
if(&that!=this){//防止自赋值
/* 小鸟版 */
delete[] m_str;//释放旧资源
m_str=new char[strlen(that.m_str)+1];//分配新资源
strcpy(m_str,that.m_str);//赋值新内容*/
//上面方法如果new失败不好处理
/*大鸟版 */
/* char* str=newchar[strlen(that.m_str)+1];//分配新资源
delete[] m_str; //释放旧资源
m_str=strcpy(str,that.m_str); //赋值新内容 */
/*老鸟版:*/
/* String temp(that);
swap(m_str,temp.m_str); */
}
return *this;
}
const char* c_str(void)const{
return m_str;
}
private:
char* m_str;
};
int main(void){
String s1("hello world!");
cout<<s1.c_str()<<endl;//hello world!
String s2=s1;//拷贝构造
cout<<s2.c_str()<<endl;//hello world!
String s3("hello C++!");
s2=s3;//拷贝赋值
cout<<s2.c_str()<<endl;//hello C++!
return 0;
}
tarena@tarena-virtual-machine:~/day40$./a.out
hello world!
hello world!
hello C++!
- Day40、this指针和常函数、析构函数、对象的创建和销毁过程、拷贝构造和拷贝赋值(深拷贝、浅拷贝!)
- day05 this指针和常函数、析构函数(Destructor) 、拷贝构造和拷贝赋值
- 深入C++的拷贝构造和赋值函数 (深拷贝,浅拷贝)
- 深入C++的拷贝构造和赋值函数 (深拷贝,浅拷贝)
- 拷贝构造函数和拷贝赋值函数
- 拷贝构造函数(深拷贝和浅拷贝)
- 拷贝构造函数(浅拷贝和深拷贝)
- c++拷贝构造函数(深拷贝和浅拷贝)
- c++拷贝构造函数(浅拷贝和深拷贝)
- c++中 拷贝构造函数的深拷贝和浅拷贝--“浅拷贝”与“深拷贝”
- 拷贝构造函数与赋值构造函数 浅拷贝和深拷贝
- 十、构造函数和析构函数(四) 拷贝构造函数、默认拷贝构造函数、拷贝构造函数调用几种情况、深拷贝浅拷贝、构造函数和=操作符区别、禁止对象拷贝
- 赋值和拷贝构造函数
- 拷贝构造和赋值函数
- 拷贝构造函数 深拷贝和浅拷贝
- 拷贝构造函数,深拷贝和浅拷贝
- 拷贝构造函数/深拷贝和浅拷贝
- 拷贝构造函数(深拷贝和浅拷贝)
- 简单文本编辑器
- 已有 a、b 两个链表,每个链表中的结点包括学号、成绩。要求把两个链表合并,按成绩升序排列。
- python爬取性感美女图片
- 环信学习文档(Java)_即时通讯
- C++ STL学习——algorithm
- Day40、this指针和常函数、析构函数、对象的创建和销毁过程、拷贝构造和拷贝赋值(深拷贝、浅拷贝!)
- 函数返回局部变量 的问题
- jenkins部署及发布使用
- 安卓微信支付
- jenkins实用插件1-Active Choices Plugin
- 《Clean Code》代码的整洁之道(二)
- C#--设计模式之建造者模式
- c++中定义的变量名称到底存在哪里了?
- Codeforces Round #373 (Div. 2) -- C - Efim and Strange Grade (贪心)