之前我们讨论过类成员的组成,尤其是成员函数,我们知道了定义一个类的时候,我们往往定义了:构造函数,析构函数,其他函数,以及友元函数(友元函数不是必须的)。

同时,我们知道了这样一个事情:在定义一个对象的时候:构造函数会被调用;对象被销毁的时候:会调用析构函数。友元函数提供给我们访问成员的另一种方式。

但这其中,会产生更多的细节。通过一些具体的细节,我们来感受一些类中的行为:

类声明如下:

 # include "iostream"
# ifndef STRNGBAD_H_
# define STRNGBAD_H_
class StringBad
{
private:
char *str;
int len;
static int num_strings; //= 0;//可见除了const 量之外,类内部成员是不能在内部赋初值的
public:
StringBad(const char * s);
StringBad();
~StringBad(); friend std::ostream & operator<<(std::ostream & os, const StringBad & st);
};
# endif

上述这个类声明中:

和通常一样:包含了构造函数,析构函数,友元函数;但这里需要强调的是 静态成员变量:num_strings。当我们使用StringBad类生成A,B,C....诸多类对象的时候,A.str,B.str,C.str....这些变量本质上地址是不同的。但是A.num_strings,B.num_strings,A.num_strings,地址是相同的。即:静态成员是属于类,而不是属于对象的。

我们看看这个类的定义:

 # include "cstring"
# include "strngbad.h" using std::cout; int StringBad::num_strings = ; StringBad::StringBad(const char*s)
{
len = std::strlen(s);
str = new char[len + ];
std::strcpy(str, s);
num_strings++;
cout << num_strings << ": \"" << str << "\" object created\n";
} StringBad::StringBad()
{
len = ;
str = new char[];
std::strcpy(str, "C++");
num_strings++;
cout << num_strings << ": \"" << str << "\" default created\n";
} StringBad::~StringBad()
{
cout << "\"" << str << "\" object created. ";
--num_strings;
cout << num_strings << "left\n";
delete[] str;
} std::ostream & operator<<(std::ostream & os, const StringBad & st)
{
os << st.str;//注意这里打印的是 str,如果不加str呢
return os;
}

关于这个类定义:我们注意这么几点:第6行进行了静态变量的初始化。注意,对num_strings 进行初始化的时候,StringBad::num_strings表明num_strings成员属于StringBad

问题:可以在类声明中进行初始化吗?

答:不可以,类声明只是说明需要为什么样的类型分配多少空间,但并不真正的分配空间,因此不能在声明中初始化(const除外)。

从这个类定义文件可以看出:无论是描述类成员函数,还是类成员变量.我们都要在前面描述其作用域!

new 和delete在动态内存分配中是十分重要的。但是要注意:在构造函数中使用了new[],在析构函数中必有delete[].

继续看调用文件:

 # include "iostream"
using std::cout;
# include "strngbad.h" void callme1(StringBad &);
void callme2(StringBad); int main()
{
using std::endl;
{
cout << "Starting an inner block.\n";
StringBad headline1("hello world!");
StringBad headline2("learning forever!");
StringBad sports("i love trvaling!");
cout << "headline1" << headline1 << endl;
cout << "headline12"<< headline2 << endl;
cout << "sports" << sports << endl;
callme1(headline1);
cout << "headline1" << headline1 << endl;
callme2(headline2);
cout << "headline2" << headline2 << endl;
cout << "Initialize one object to another:\n";
StringBad sailor = sports;
cout << "sailor: " << sailor << endl;
}
cout << "End of main()\n";
system("pause");
return ;
} void callme1(StringBad & rsb)
{
cout << "String passed by reference:\n";
cout << " \n" << rsb << "\"\n";
} void callme2(StringBad sb)
{
cout << "String passed by rvalue:\n";
cout << " \n" << sb << "\"\n";
}

在此,给出第19行代码和第21行代码的一些细节:

第19行代码,传递参数的行为是引用传递,21行为:值传递。

再次强调:值传递 和 值返回 函数的两个本质特征:1. 进行值传递的时候,复制实参的副本,使得形参为实参的副本;2. 值返回的时候,复制返回值的副本(并销毁该变量(因为栈中的变量生存周期为函数调用期)),对副本进行后续操作。

因此第21行的代码:在进行按值传递的时候,首先创建一个对象的副本。但在创建对象副本的时候,首先会调用构造函数,但是这个构造函数是谁呢?是之前类定义中的构造函数吗?如果是,显然这个创建副本的形式应该为:()或者(cha*),但问题是:这里是吗???显然不是,那是什么呢???其实为:B=A,即将已知的对象A赋值给临时对象B,这就如同第24行的代码。那么这样的初始化方式,调用哪个构造函数呢???其实,是一种叫做赋值构造函数,专门应对这种赋值初始化的操作。很明显,之前的声明和定义中并没有赋值构造函数。那么这个赋值构造函数只能由编译器产生。

终于知道为何开始要求我们采用()或者{}的初始化方式,而不是"="的初始化方式。因为通常的我们可能并没有显示的定义自己的赋值构造函数,这个时候由编译器产生一个这样的函数,可能会存在一定的风险。但如果我们不可避免会采用到=或者值传递创建函数的形式,我们应该显示的定义自己的赋值构造函数,使得我们的程序风险更低。(实际上,通常的定义一个显示赋值构造函数更保险)

总结:按值传递和按值返回对象的时候,都将调用赋值构造函数!2构造函数中创建一个显示赋值构造函数通常更保险

我们在此再次声明:“hello World!”表示一个地址而不是字符串。当我们定义:p = “hello Word!”,假如我们定义:q = p,那么:是否是将字符串P复制了一遍,并存到了q的地址呢?并不是,而是:p,q都指向了“hello world!”,而这个时候,“hello world! ”其实看起来更像地址。

c++入门之详细探讨类的一些行为的更多相关文章

  1. c++入门之再次探讨类属性

    精辟博文:https://blog.csdn.net/msdnwolaile/article/details/51923859(转载,仅供学习|!)

  2. c++ 入门之深入探讨拷贝函数和内存分配

    在c++入门之深入探讨类的一些行为时,说明了拷贝函数即复制构造函数运用于如下场景: 对象作为函数的参数,以值传递的方式传给函数. 对象作为函数的返回值,以值的方式从函数返回 使用一个对象给另一个对象初 ...

  3. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  4. Python编程从入门到实践笔记——类

    Python编程从入门到实践笔记——类 #coding=gbk #Python编程从入门到实践笔记——类 #9.1创建和使用类 #1.创建Dog类 class Dog():#类名首字母大写 " ...

  5. python入门学习:8.类

    python入门学习:8.类 关键点:类 8.1 创建和使用类8.2 使用类和实例8.3 继承8.4 导入类 8.1 创建和使用类   面向对象编程是最有效的软件编写方法之一.在面向对象编程中,你编写 ...

  6. discuz插件开发新手入门 超详细

    作为一个新手,目前也是刚刚玩转discuz的插件功能,好东西不敢独享,就拿出来大家一起分享入门的过程.现在网上很多关于discuz的插件教程都是很简单的教程,原因可能是这个东西是商业化的东西,本着分享 ...

  7. SpringBoot入门最详细教程

    monkey01 关注 2017.08.08 13:36* 字数 1479 阅读 34248评论 0喜欢 15 网上有很多springboot的入门教程,自己也因为项目要使用springboot,所以 ...

  8. SpringMVC札集(03)——基于注解的SpringMVC入门完整详细示例

    自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...

  9. Python 3 快速入门 3 —— 模块与类

    本文假设你已经有一门面向对象编程语言基础,如Java等,且希望快速了解并使用Python语言.本文对重点语法和数据结构以及用法进行详细说明,同时对一些难以理解的点进行了图解,以便大家快速入门.一些较偏 ...

随机推荐

  1. 在CentOS上配置SAMBA共享目录(转载)

    在CentOS上配置SAMBA共享目录 From: https://blog.csdn.net/qiumei1101381170/article/details/53265341 2016年11月21 ...

  2. web前端(2)—— 前端技术介绍

    前端技术 前文了解了什么是前端,那么前端技术到底有哪些呢?最核心的就这三个: html/html5 css/css3 javascript 什么是HTML HyperText Markup Langu ...

  3. adb入门学习笔记

    连接模拟器(模拟器桥接模式) 使用adb devices列出已连接到工作站的设备. 使用adb shell 启动设备或模拟器上的shell. 列出设备已安装的所有软件包 将电脑文件移动到手机模拟器上( ...

  4. Java的基础知识一

    一.什么是 Java 技术?为何需要 Java? Java 是由 Sun Microsystems 在 1995 年首先发布的编程语言和计算平台.有许多应用程序和 Web 站点只有在安装 Java 后 ...

  5. GitLab安装及使用

    GitLab是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目. GitLab拥有与Github类似的功能,能够浏览 ...

  6. Win 10 和 Linux 双系统,从硬盘删除Linux分区,Win 10引导修复

    由于安装双系统后,Linux 用的比较少.因此,从Win 10 磁盘管理中删除了linux 占用的磁盘空间,重启后无法进入win 10 ,出现如下情况: 有人提出,此时需要重装系统,并不用如此麻烦,通 ...

  7. LeetCode算法题-Binary Watch(Java实现)

    这是悦乐书的第216次更新,第229篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第84题(顺位题号是401).二进制手表顶部有4个LED,代表小时(0-11),底部的6 ...

  8. Docker: docker container常用命令实战

    容器管理,容器常用选项 选项 描述 -i, –interactive 交互式 -t, –tty 分配一个伪终端 -d, –detach 运行容器到后台 -e, –env 设置环境变量 -p, –pub ...

  9. 关于connect by rownum与connect by leve

    http://www.itpub.net/forum.php?mod=viewthread&tid=1570306 http://www.itpub.net/forum.php?mod=vie ...

  10. P2819 图的m着色问题(DFS)

    思路:最开始的回溯顺序是正常的图遍历的回溯顺序,其实也没有错.但是,因为怎么调都不对,看了题解.下面,请结合题解思路和代码一起感受一下回溯顺序的改变,算法的改变和代码在哪里实现了这种顺序. 回溯顺序: ...