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 ...
随机推荐
- noi.ac #543 商店
我们考虑可并堆维护,从深到浅贪心选取. 用priority_queue启发式合并的话,是60pts: #include<iostream> #include<cstdio> # ...
- angularJS限制 input-text 只能输入数字
最初的目的是为了让输入的字段仅为数字,不要包含英文,理所当然想到了正则表达,比起对每一个字符判断ASCII码要方便的多 JS正则表达式的使用,可以参考正则表达式 以match为例,全局匹配非数字 va ...
- 两个线程,一个线程打印1~52,另一个线程打印字母A-Z,打印顺序为12A34B56C……5152Z
使用wait,notify实现 public class Test { public synchronized void a() { for (int i = 1; i <= 52; i++) ...
- Python基础之变量和常量
变量 将运算得到中间结果暂存到内存,以便后续程序调用. 变量的命名规则: 变量由字母.数字.下划线搭配组合而成 不能以数字开头,更不能全是数字 不能是Python中的关键字,这些符号和字母已经被Pyt ...
- 11.二进制中1的个数 Java
题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 思路 当n不等于0时执行以下循环: 1.判断n的最低位是否为1,若为1,则计数器加1 2.将n无符号右移1位(若使用带符号移 ...
- HighCharts 动态设置 series
var series = new Array(); var map = response.extend.map; $.each(map, function (key,values) { series. ...
- FastAdmin 在 CRUD 时出现 exec() has been disabled for security reasons 处理方法
然后在看看 禁用函数列表(php.ini)disable_functions = proc_open, popen, exec, system, shell_exec, passthru 这里要把 e ...
- spring boot配置redis
- oracle中删除某个用户下的所有表
一般的方法:先使用sql查询: SELECT 'DELETE FROM '|| table_name || ';' FROM USER_TABLES ORDER BY TABLE_NAME; 将查询结 ...
- jsp页面中使用javabean
<%@ page language="java" import="java.util.*,com.loaderman.demo.b_cases.*" pa ...