C++系列笔记(三)

简介: C++系列笔记(三)

 【导读21天学通C++》这本书通过大量精小短悍的程序详细而全面的阐述了C++的基本概念和技术,包括管理输入/输出、循环和数组、面向对象编程、模板、使用标准模板库以及创建C++应用程序等。这些内容被组织成结构合理、联系紧密的章节,每章都可在1小时内阅读完毕,都提供了示例程序清单,并辅以示例输出和代码分析,以阐述该章介绍的主题。 本文是系列笔记的第三篇,主要讲的是类、对象、析构函数等知识,欢迎各位阅读指正!

1、类和对象

声明类使用关键字class,并在他后面依次包含类名、一组放在{ }内的成员属性和方法以及结尾的分号。

classHuman{
//Data attributes:stringName;
stringGender;
//Methods:voidTalk(stringTextToTalk);
voidIntroduceYouself( );
……} ;

就像int分配动态内存一样,也可以使用new为Human对象动态的分配内存;

Human*pAnotherHuman=newHuman();  //动态的分配内存deletepAnotherHuman;

使用句点运算符来访问成员

HumanTom;
Tom.DateBirth="1970"

使用指针运算符(->)访问成员 如果对象是使用new在自有储存区中实例化的,或者有指向对象的指针,则可以使用指针运算符(->)来访问成员属性和方法。

Human*pTom=newHuman();
pTom->DataOfBirth="1970";
pTom->IntroduceYouself();
deletepTom;

下面结合代码来看一下:

#include<iostream>#include<string>usingnamespacestd;
classHuman        {
private:
stringName;
intAge;
public:
voidSetName(stringHumansName)
               {
Name=HumansName;
               }
voidSetAge(intHumansAge)
               {
Age=HumansAge;
               }
voidIntroduceSelf()
               {
cout<<"I am"+Name<<"and am";
cout<<Age<<"years old"<<endl;
               }
        };
intmain()
        {
HumanFirstMan;
FirstMan.SetName("Adam");
FirstMan.SetAge(30);
FirstMan.IntroduceSelf();
        }

2、构造函数

构造函数是一种特殊的函数,它与类同名且不返回任何值。因此,Human类在声明内的构造函数声明类似于下面:

classHuman{
public:
Human( )
    {
//代码    }
}

在类声明外定义构造函数的代码如下:

classHuman{
public:
Human( );
 };
Human :: Human()
 {
//代码 }

::被称为作用域解析运算符。例如:Human::DateOfBirth指的是在Human类中声明的变量DateOfBirth,而::DateOfBirth表示全局作用域中的变量DateOfBirth。包含初始化列表的构造函数的代码如下:

classHuman{
public:
Human(stringInputName="Adam" , intAge=25 )
            : Name(InputName) , Age(InputAge)
  {
//代码  }
}

初始化列表由包含在括号当中的参数声明后面的冒号标识,冒号后面列出了各个成员变量及其初始化值。初始化值可以是参数,也可以是固定的值。

3、析构函数

析构函数在对象销毁时自动被调用。析构函数看起来也像一个与类同名的函数,但前面有一个波浪号(~)。因此,Human类的析构函数声明类似于下面这样,这是在类里面声明:

classHuman{
public:
~Human( )
    {
// code here    }
};

在类声明外定义析构函数:

classHuman{
public:
~Human();
};
Human::~Human()
{
//code here}

析构函数与构造函数的作用完全相反。析构函数是重置变量以及释放动态分配的内存和其他资源的理想场所。析构函数典型代码如下:

#include<iostream>#include<string>usingnamespacestd;
classMyString{
private:
char*Buffer;
public:
MyString(constchar*InitialInput)
        {
if (InitialInput!=NULL)
               {
Buffer=newchar[strlen(InitialInput+1)];
strcpy(Buffer, InitialInput);
               }
elseBuffer=NULL;
        }
~MyString()
        {
cout<<"Invoking destructor,cleaning up"<<endl;
if (Buffer!=NULL)
delete[] Buffer;
        }
intGetLength()
        {
returnstrlen(Buffer);
        }
constchar*GetString()
        {
returnBuffer;
        }
};
intmain()
{
MyStringSayHello("Hello from string class:");
cout<<"string buffer in MyString is:"<<SayHello.GetLength();
cout<<"characters long"<<endl;
cout<<"Buffer contains:";
cout<<"Buffer contains:"<<SayHello.GetString();
}

程序运行输出为:

stringbufferinMyStringis:24characterslongBuffercontains:Buffercontains:Hellofromstringclass:Invokingdestructor,cleaningup

析构函数不能重载,每个类都只能有一个析构函数。如果你忘记实现一个析构函数,编译器将创造一个伪(dummy)析构函数并调用他。伪析构函数为空,既不释放动态分配的内存。

复制构造函数

浅复制:复制类对象时,将复制其指针成员,都不复制指针指向的缓冲区,造成两个对象指向同一块动态分配的内存,会威胁程序的稳定性。深复制:所以要将浅复制的参数复制变成地址传递,即按参数引用传递而不是进行二进制复制。代码示例如下

#include<iostream>#include<string>usingnamespacestd;
classMyString{
private:
char*Buffer;
public:
MyString(constchar*InitialInput)
        {
cout<<"Constructor:creating new String"<<endl;
if (InitialInput!=NULL)
               {
Buffer=newchar[strlen(InitialInput+1)];
strcpy(Buffer, InitialInput);
cout<<"Buffer points to:0x"<<hex;
cout<< (unsignedint*)Buffer<<endl;
               }
elseBuffer=NULL;
        }
//复制构造函数MyString(constMyString&CopySource)
        {
cout<<"copy constructeor:copy from MyString"<<endl;
f (CopySource.Buffer!=NULL)
               {
Buffer=newchar[strlen(CopySource.Buffer) +1];
strcpy(Buffer, CopySource.Buffer);
cout<<"Buffer points to:0x"<<hex;
cout<< (unsignedint*)Buffer<<endl;
               }
elseBuffer=NULL;
        }
~MyString()
        {
cout<<"Invoking destructor,cleaning up"<<endl;
if (Buffer!=NULL)
delete[] Buffer;
        }
intGetLength()
        {
returnstrlen(Buffer);
        }
constchar*GetString()
        {
returnBuffer;
        }
};
voidUseMyString(MyStringInput)
{
cout<<"Sting Buffer in mystring is"<<Input.GetLength();
cout<<"characters long"<<endl;
cout<<"Buffer contains:"<<Input.GetString() <<endl;
return;
}
intmain()
{
MyStringSayHello("Hello from string class:");
UseMyString(SayHello);
}

程序运行输出:

Constructor:creatingnewStringBufferpointsto:0x004BD5A0copyconstructeor:copyfromMyStringBufferpointsto:0x004BD5E8StingBufferinmystringis18characterslongBuffercontains:Hellofromstringclass:
Invokingdestructor,cleaningupInvokingdestructor,cleaningup

MyString包含原始指针成员char* Buffer,一般不要为类成员声明原始指针,而应该使用std::string。在没有原始指针的情况下,都不需要编写复制构造函数,这是因为编译器添加的默认复制构造函数将调用成员对象(如:std::string)的复制构造函数。

今天的内容就到这里,我们下次再见啦!

相关文章
|
7月前
|
算法 C++
算法笔记:递归(c++实现)
算法笔记:递归(c++实现)
|
7月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
|
5月前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
33 1
|
5月前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
75 0
|
6月前
|
C++ Windows
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
在Windows上使用Visual Studio 2022进行FFmpeg和SDL2集成开发,首先安装FFmpeg至E:\msys64\usr\local\ffmpeg,然后新建C++控制台项目。在项目属性中,添加FFmpeg和SDL2的头文件及库文件目录。接着配置链接器的附加依赖项,包括多个FFmpeg及SDL2的lib文件。在代码中引入FFmpeg的`av_log`函数输出"Hello World",编译并运行,若看到"Hello World",即表示集成成功。详细步骤可参考《FFmpeg开发实战:从零基础到短视频上线》。
256 0
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
|
8月前
|
存储 C++ 容器
黑马c++ STL部分 笔记(7) list容器
黑马c++ STL部分 笔记(7) list容器
|
8月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
|
8月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(4) 常用拷贝和替换算法
黑马c++ STL常用算法 笔记(4) 常用拷贝和替换算法
|
8月前
|
存储 算法 搜索推荐
黑马c++ STL常用算法 笔记(3) 排序算法
黑马c++ STL常用算法 笔记(3) 排序算法
|
8月前
|
算法 C++
黑马c++ STL常用算法 笔记(2) 查找算法
黑马c++ STL常用算法 笔记(2) 查找算法