C++学习 之 类中的特殊函数和this指针(笔记)
1.构造函数
构造函数是一种特殊的函数,它在对象被创建时被调用,与类同名无返回类型,可以被重载。构造函数的可以在类内实现也可以在类外实现。
构造函数的声明类似于下面的代码:
class Human
{
public:
Human();//构造函数声明
};
构造函数在类声明中实现类似于下面的代码:
class Human
{
public:
Human ()
{
//构造函数的实现部分
}
};
构造函数在类的声明外实现类似于下面的代码:
class Human
{
public:
Human ();
};
Human::Human()
{
//构造函数的实现部分
};
使用构造函数便于我们对类内的属性进行初始化,确保已知属性中不包含随机值。构造函数也是可以重载的,这说明在调用构造函数时有时是需要提供参数的,可以在不提供参数的情况下调用的构造函数
称作默认构造函数。在调用构造函数初始化对象的属性时,又需要提供不同的参数,这时重载构造函数就可以为我们提供帮助。
构造函数的重载类似于下面的代码:
class Human
{
private:
string Name;
int Age;
public:
Human (int HumanAge)
{
Age=HuanAge;
}
Human (string HumanName,int HumanAge)
{
Name=HumanName;
Age=HumanAge;
}
};
其实构造函数不仅可以重载,其参数还可以带有默认值。
例如下面这样:
class Human
{
private:
string Name;
int Age;
public:
Human (int HumanAge)
{
Age=HuanAge;
}
Human (string HumanName,int HumanAge=)//与上一段代码的区别在于给出了HumanAge的默认值
{
Name=HumanName;
Age=HumanAge;
}
/*Human (int HumanAge,string HumanName="Tom")//去掉多行注释符号后,这种重载会报错,原因在于当提供一个int型的参数调用构造函数时,系统并不知道该调用Human(int)Human(int,string HumanName="Tom")
{
Name=HumanName;
Age=HumanAge;
}*/
};
2.析构函数
与析构函数一样,析构函数也看上去和类同名,只是在函数名前多了波浪号"~"。每当对象不再在作用域内或通过delete被删除,进而被销毁时都将调用析构函数。这使得析构函数是重置变量以及释放动态分配的内存和其他资源的理想场所。析构函数不能够重载每个类只能有一个析构函数,如果忘记实现析构函数,系统会自动生成一个伪析构函数,由于伪析构函数为空其不能释放动态分配的内存空间。
析构函数的类内实现类似于下面的代码:
class Human
{
public:
~Human()
{
//析构函数实现部分
} };
析构函数的类外实现类似于下面的代码:
class Human
{
public:
~Human();
};
Human::Human()
{
//析构函数实现部分
};
对象所在的函数已调用完毕时,系统自动执行析构函数;用new开辟了一片内存空间,delete会自动调用析构函数后释放内存;对象A是对象B的成员,B的析构函数被调用时,对象A的析构函数也会被调用。
3.复制构造函数
3.1浅复制及其存在的问题:
当类中包含指针成员,定义的类的对象又作为实参传递给某个函数的形参(即被复制)时,指针成员将被复制。复制后的指针成员和原对象中的指针成员指向同一个内存空间,这被称为浅复制,会威 胁程序的稳定性。
例如下面的程序就存在问题:
#include<iostream>
#include<cstring>
using namespace std;
void strcpy_s(char* pre, const char* next)
{
if (next != NULL)
{
while ((*pre++ = *next++) != '\0');
}
}
class MyString
{
private:
char* Buffer;
public:
MyString(const char *InitialInput)
{
if (InitialInput != NULL)
{
Buffer = new char[strlen(InitialInput) + ];//Buffer指向新分配的空间
strcpy_s(Buffer, InitialInput);
}
else
Buffer = NULL;
}
~MyString()
{
if(Buffer!=NULL)
{
cout << "析构,释放内存" << endl;
if (Buffer != NULL)
delete[] Buffer;
}
}
int GetLength()
{
return strlen(Buffer);
}
const char* GetString()
{
return Buffer;
}
};
void UseMyString(MyString Input)//调用该函数时实参对象会被复制给形参Input
{
cout << "Buffer 字数为:" << Input.GetLength();
cout << "Buffer 内容为:" << Input.GetString();
return;
}
int main()
{
MyString SayHello("Hello");
UseMyString(SayHello);
return ;
}
在调用UseMyString函数时,实参SayHello会被浅复制给形参Input。在传递参数的过程中,SayHello里有个成员指针Buffer,SayHello.Buffer会把地址传给Input.Buffer(这就像之前的函数的参数的传递方式一样);这样会导致它们指向同一块内存空间。当UseMyString函数调用完成时会调用析构函数释放Inpu的内存空间,然后返回主函数,当主函数执行完成后会调用析构函数释放SayHello的内存空间,这时便会出现问题,因为Input.Buffer和SayHello.Buffer指向了同一块内存空间,此时却连续执行了两次析构函数释放了同一块内存空间(程序会崩溃或无法返回正常值)。
浅复制存在的最大问题便是会可能出现复制出的对象与原对象共用某块内存的现象,这样很可能会在释放所占内存时出问题。我们便引入复制构造函数进行深度复制解决类似问题。
3.2 使用复制构造函数:
复制构造函数是一种特殊的重载构造函数,我们在使用类时必须提供它。每当对象被复制其中包括把对象当作参数按值传递给函数时,编译器都将调用复制构造函数。复制构造函数接受一个以引用方 式传入的当前类的对象作为参数。这个参数是源对象的别名,我们在构造函数内使用它来复制源对象,确保对所有缓冲区进行复制。
下面给出上面浅复制修改为深复制的代码:
#include<iostream>
#include<cstring>
using namespace std;
void strcpy_s(char* pre, const char* next)
{
if (next != NULL)
{
while ((*pre++ = *next++) != '\0');
}
}
class MyString
{
private:
char* Buffer;
public:
MyString(const char* InitialInput)
{
if (InitialInput != NULL)
{
Buffer = new char[strlen(InitialInput) + ];
strcpy_s(Buffer, InitialInput);
}
else
Buffer = NULL;
}
MyString(const MyString& CopySource)//复制构造函数
{
cout <<"从源对象复制:"<< endl;
if (CopySource.Buffer != NULL)
{
Buffer = new char[strlen(CopySource.Buffer) + ];
strcpy_s(Buffer, CopySource.Buffer);
}
else
Buffer = NULL;
}
~MyString()
{
if(Buffer!=NULL)
{
cout << "析构,释放内存" << endl;
if (Buffer != NULL)
delete[] Buffer;
}
}
int GetLength()
{
return strlen(Buffer);
}
const char* GetString()
{
return Buffer;
}
};
void UseMyString(MyString Input)
{
cout << "Buffer 字数为:" << Input.GetLength()<<endl;
cout << "Buffer 内容为:" << Input.GetString()<<endl;
return;
}
int main()
{
MyString SayHello("Hello");
UseMyString(SayHello);
return ;
}
4.this指针
在C++中,一个重要的概念是保留的关键字this,在类中,关键字this包含当前对象的地址,换句话说,其值为&object。当您在类成员方法中调用其他成员方法时,编译器将隐式地传递this指针——函数 调用中不可见的参数:
#include<iostream>
#include<cstring>
using namespace std;
class Human
{
private:
int Age;
char Name;
public:
void SetAge(int HumansAge)
{
Age = HumansAge;//修改当前对象的Age值
cout<<Age;
}
};
int main()
{
Human FirstMan;
FirstMan.SetAge();
return ;
}
在上面对象FirstMan调用类中静态函数时就存在着this指针的隐式传递,即调用FirstMan(21)相当于相当于FirstMan(this,21);对对象成员Age的访问相当于this->Age=HumanAge。在整个过程中this指向当前对象,存储当前对象的地址。
C++学习 之 类中的特殊函数和this指针(笔记)的更多相关文章
- PHP面向对象学习五 类中接口的应用
类中接口的应用 接口:一种成员属性全部为抽象的特殊抽象类,在程序中同为规范的作用 抽象类:1.类中至少有一个抽象方法.2.方法前需要加abstract 接口: 1.类中全部为抽象方法,抽象方法前不 ...
- Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)
当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...
- python学习-- class 类中需要注意的地方
from django.db import models class Person(models.Model): name = models.CharField(max_length=30) ...
- mfc 在VC的两个对话框类中传递参数的三种方法
弄了好久,今天终于把在VC中的对话框类之间传递参数的问题解决了,很开心,记录如下: 1. 我所建立的工程是一个基于MFC对话框的应用程序,一共有三个对话框,第一个对话框为主对话框,所对应的类为CTMD ...
- Scala学习(五)---Scala中的类
Scala中的类 摘要: 在本篇中,你将会学习如何用Scala实现类.如果你了解Java或C++中的类,你不会觉得这有多难,并且你会很享受Scala更加精简的表示法带来的便利.本篇的要点包括: 1. ...
- python中的面向对象学习以及类的继承和继承顺序
继承 首先编写一串关于类的代码行: __author__ = "Yanfeixu" # class People: 经典类不用加(object) class People(obje ...
- Java学习笔记(七)——获取类中方法的信息,java的LinkedList
[前面的话] 在实际项目中学习知识总是最快和最有效的,既能够较好的掌握知识,又能够做出点东西,还是简单的知识总结,最近一直在总结笔记,写的东西还是比较水,希望慢慢可以写出一些干货. 学习过程中的小知识 ...
- Cocos2d-x 3.1.1 学习日志2--error:仅仅有静态常量整型数据成员才干够在类中初始化
今天遇到比較低端的一个问题,就是成员的初始化问题,编译器也无法验证,不同的编译器有些能过有些不能过,我也不知道为什么,总是我们以vs为准吧,以为我们用的环境就是它,话不多说.解决方式例如以下: ...
- Effective C++学习笔记:初始化列表中成员列出的顺序和它们在类中声明的顺序相同
类成员的默认初始化顺序是按照声明顺序进行, 如果使用初始化列表初始化成员变量, 则必须按照成员变量的声明顺序进行; 否则, 在变量之间交替赋值时, 会产生, 未初始化的变量去赋值其他变量; 同时GCC ...
随机推荐
- R-ts()
概述 ts(gm,frequency=12,start=c(1975,1)) 这个命令表示: frequency=12表明时间单位为年,而且在每一个时间单位中有12个均匀间隔的观察值. 因此gm是 ...
- 浅析SPDY
1.什么是SPDY? 简单地说,在SSL层上增加一个SPDY会话层,以在一个TCP连接支持并发的HTTP请求.也就是他能通过复用仅仅一条(或几条)TCP连接,在客户端与服务器间发送几十个请求或回应. ...
- iframe 跨域传参
parent-index.html: (本地起服务,放在5000端口上) <div class="content"> <iframe src="http ...
- Php+Redis函数使用总结
因项目需求,冷落了redis,今天再重新熟悉一下: <?php //连接 $redis = New Redis(); $redis->connect('127.0.0.1','6379', ...
- TLS Thread Local Storage
https://blog.csdn.net/yusiguyuan/article/details/22938671 https://blog.csdn.net/simsunny22/article/d ...
- EXCEL中自定义格式输入的数据怎么完整复制
在用设置单元格式里 自定义 输入数值 如图,B列的数据,我复制后,用选择性粘贴到别的地方,还是无法将75FG4Y2一起复制过去,只能复制过去FG 怎么办? ===>先把这些复制到一个记事本里,再 ...
- 2-mybatis-启动
启动相关的类主要位于session包下. 参照mybatis官方文档,从xml中构建SqlSessionFactory后,可以获取SqlSession,然后使用SqlSession查询数据库得到结果. ...
- 利用CountDownTimer倒计时的简单使用实现
package com.loaderman.countdowntimerdemo; import android.os.Bundle; import android.os.CountDownTimer ...
- tensorflow实现验证码识别案例
1.知识点 """ 验证码分析: 对图片进行分析: 1.分割识别 2.整体识别 输出:[3,5,7] -->softmax转为概率[0.04,0.16,0.8] - ...
- phpstrom--------config php interpreter
phpstrom是一款比较好用的php代码编辑器,使用phpstrom进行代码编辑时我可能会需要看一下在网页上的实际效果,但是PHPstrom本身只是一款编辑器,不具备运行功能,我们需要自己安装一个服 ...