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指向当前对象,存储当前对象的地址。

 :关于this指针的一个经典回答:
  当你进入一个房子后,
  你可以看见桌子、椅子、地板等,
  但是房子你是看不到全貌了。
  对于一个类的实例来说,
  你可以看到它的成员函数、成员变量,
  但是实例本身呢?
  this是一个指针,它时时刻刻指向你这个实例本身。

  

  

  

C++学习 之 类中的特殊函数和this指针(笔记)的更多相关文章

  1. PHP面向对象学习五 类中接口的应用

    类中接口的应用 接口:一种成员属性全部为抽象的特殊抽象类,在程序中同为规范的作用   抽象类:1.类中至少有一个抽象方法.2.方法前需要加abstract 接口: 1.类中全部为抽象方法,抽象方法前不 ...

  2. Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)

    当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...

  3. python学习-- class 类中需要注意的地方

    from django.db import models class Person(models.Model):     name = models.CharField(max_length=30) ...

  4. mfc 在VC的两个对话框类中传递参数的三种方法

    弄了好久,今天终于把在VC中的对话框类之间传递参数的问题解决了,很开心,记录如下: 1. 我所建立的工程是一个基于MFC对话框的应用程序,一共有三个对话框,第一个对话框为主对话框,所对应的类为CTMD ...

  5. Scala学习(五)---Scala中的类

    Scala中的类 摘要: 在本篇中,你将会学习如何用Scala实现类.如果你了解Java或C++中的类,你不会觉得这有多难,并且你会很享受Scala更加精简的表示法带来的便利.本篇的要点包括: 1. ...

  6. python中的面向对象学习以及类的继承和继承顺序

    继承 首先编写一串关于类的代码行: __author__ = "Yanfeixu" # class People: 经典类不用加(object) class People(obje ...

  7. Java学习笔记(七)——获取类中方法的信息,java的LinkedList

    [前面的话] 在实际项目中学习知识总是最快和最有效的,既能够较好的掌握知识,又能够做出点东西,还是简单的知识总结,最近一直在总结笔记,写的东西还是比较水,希望慢慢可以写出一些干货. 学习过程中的小知识 ...

  8. Cocos2d-x 3.1.1 学习日志2--error:仅仅有静态常量整型数据成员才干够在类中初始化

        今天遇到比較低端的一个问题,就是成员的初始化问题,编译器也无法验证,不同的编译器有些能过有些不能过,我也不知道为什么,总是我们以vs为准吧,以为我们用的环境就是它,话不多说.解决方式例如以下: ...

  9. Effective C++学习笔记:初始化列表中成员列出的顺序和它们在类中声明的顺序相同

    类成员的默认初始化顺序是按照声明顺序进行, 如果使用初始化列表初始化成员变量, 则必须按照成员变量的声明顺序进行; 否则, 在变量之间交替赋值时, 会产生, 未初始化的变量去赋值其他变量; 同时GCC ...

随机推荐

  1. Java根据余弦定理计算文本相似度

    项目中需要算2个字符串的相似度,是根据余弦相似性算的,下面具体介绍一下: 余弦相似度计算 余弦相似度用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小.余弦值越接近1,就表明夹角越接近0度, ...

  2. AcWing:142. 前缀统计(字典树)

    给定N个字符串S1,S2…SNS1,S2…SN,接下来进行M次询问,每次询问给定一个字符串T,求S1S1-SNSN中有多少个字符串是T的前缀. 输入字符串的总长度不超过106106,仅包含小写字母. ...

  3. Selenium定位策略

    1.通过XPath使用contains() 它将启动一个窗口,其中包含文本框开发中涉及的所有特定代码. 记下它的id属性. 通过XPath定位元素的语法 - 使用contains()可以写成: //& ...

  4. 邻居子系统 之 更新neigh_update

    概述 neigh_update函数用来更新指定的邻居项,更新内容是硬件地址和状态,更新之后,会根据新状态设置其输出函数,CONNECTED状态则使用快速输出,否则使用慢速输出:如果是由原来的无效状态变 ...

  5. react 脚手架 及路由和 redux

    前提是我们需要下载 nodejs 使用 npm 下载 react 的脚手架,react-router-dom,redux 全局下载 react 的脚手架:npm i create-react-app ...

  6. mvp解读

    mvp存在的问题 1.业务复杂时,可能使得Activity变成更加复杂,比如要实现N个IView,然后写更多个模版方法. 2.业务复杂时,各个角色之间通信会变得很冗长和复杂,回调链过长. 3.Pres ...

  7. 一个继承的 DataGridView

    // 允许增加一个 checkbox 列 public class DgvBase : DataGridViewX { protected override void OnColumnAdded(Da ...

  8. SSD 页、块、垃圾回收

    基本操作: 读出.写入.擦除: 因为NAND闪存单元的组织结构限制,单独读写一个闪存单元是不可能的.存储单元被组织起来并有着十分特别的属性.要知道这些属性对于为固态硬盘优化数据结构的过程和理解其行为来 ...

  9. 排查python内存泄露中几个工具的使用

    本文主要介绍3个工具:pdb,objgraph,以及pympler. 1.pdb pdb是专门用于python代码调试,模仿gdb. 使用pdb可以查看堆栈,打印变量等. 这里介绍的是命令行下的pdb ...

  10. vue 默认展开详情页

    { path: '/Tree', component: Tree, children: [ { path: '/', component: Come } ] }