1-->类  命名空间

1.0  复习构造函数:1  与类同名   2  没有返回值   3  自动生成    4  手动后,不会自动生成    5  不在特定的情况下,不会私有

1.1   新建    两种方法示范  其一:在vs中选择类,编译器帮我们生成一部分默认代码   其二:手动添加头文件,添加cpp文件。

1.2  域作用符::

1.3  看上去生成了空类,注意不能跨平台的代码。

1.4  自动生成的头文件,是要手动修改的 :  #pragma once    //这是windows中的特有表示,要替换成下面的写法。

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ class ClassDemo
{
public:
ClassDemo();
~ClassDemo();
}; #endif //!_CLASSDEMO_H_

1.5  最佳的写法,还要加上命名空间:

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{class ClassDemo
{
public:
ClassDemo();
~ClassDemo();
};
}
#endif //!_CLASSDEMO_H_

1.6  加入命名空间,能很好的避免类同名的问题。


2-->类  命名空间 污染

2.0  命名空间不建议写成using name space XXXXX ,因为要考虑命名空间污染的问题:

2.1  什么是命名空间污染呢?  

2.2  关于#include <>和#include " " 这两种运用的解释:<>表示先在系统目录里面查找头文件,“ ”则表示先在当前目录(工程目录)查找头文件。一般标准库用<>号,我们自己的头文件用“”号。

2.3  string.h里面还加一个命名空间:namespace PoDdu

#ifndef _STRING_H_
#define _STRING_H_ namespace PoEdu
{
class string
{ };
} #endif //!_STRING_H_

  string.cpp里面写上:

#include "ClassDemo.h"
#include "string.h"
#include <iostream>
#include <string>
using namespace std;
using namespace PoEdu; int main()
{
string stdstring;
PoEdu::string poedustring;
cout << stdstring; return ;
}

看图

所以在主函数所在的界面,最好不要写全局的using namespace XXXX

2.4  一定要写,可以写在别的cpp里面,如:ClassDemo.cpp

#include "ClassDemo.h"
using namespace PoEdu; ClassDemo::ClassDemo()
{
} ClassDemo::~ClassDemo()
{
}

这样子就不会影响全局,出现命名空间污染。命名空间其实就是用来区分“组织”的。

2.5  再举例:微软和谷歌都开发了一套库函数,里面都有string和system,那么想要同时使用两个组织的库,如果没有一套命名规则,那么肯定的重名,因为重名了,永远编译不过去。

加上命名空间,就避免了重名:

所以全局界面,少用using namespace XXXX;


3-->类  头文件  include包含原则

3.0  再来看:ClassDemo.cpp这个include<>它写在cpp文件里面的,为什么不写头文件那边呢?

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} ClassDemo::~ClassDemo()
{
}
}

3.1  我们的头文件编译器最终在主函数之前,遇到#include “xxx” 的时候会展开,如果头文件开头就包含了太多的东西,编译器的压力就大了很多,特别是包含了很多本地的如:反复的#include "xxx" 多个,那么就都要一个个的展开来,一个个来判断它是否重复。所以头文件里面不需要使用的,就不写在头文件里面。  但系统级的#include <xxx>可以有限使用,要用到就包含。如果一定要包含我们自己写的头文件,最好的写法是:


4-->类  构造函数  

ClassDemo.h

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
ClassDemo(int num);
~ClassDemo();
int GetNum();
private:
int _num;
};
}
#endif //!_CLASSDEMO_H_

ClassDemo.cpp

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} ClassDemo::ClassDemo(int num )
{
std::cout << "ClassDemo(" << num << ")"<< std::endl;
} ClassDemo::~ClassDemo()
{
} int ClassDemo::GetNum()
{
return _num;
}
}

main.cpp

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu;
ClassDemo demo;
ClassDemo demo1();
std::cout << demo.GetNum() << std::endl;
std::cout << demo1.GetNum() << std::endl; return ;
}

运行:

4.1  ClassDemo.cpp里面代码少了赋值,加上_num = num ;

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} ClassDemo::ClassDemo(int num )
{
_num = num;
std::cout << "ClassDemo(" << num << ")"<< std::endl;
} ClassDemo::~ClassDemo()
{
} int ClassDemo::GetNum()
{
return _num;
}
}

再次运行:

4.2  在debug版本下面,未定义默认在内存给出0xcccccccc方便调试 ,所以得出同一个数值。在Release版本下面:


5--> new  delete

5.0  有没有这种方式:ClassDemo *demo = new ClassDemo ;  答:有的。这种方式也会调用构造函数。

5.1  重点来了:这里用一个对象指针,new 了一个ClassDemo对象,构造函数如期运行了,析构函数有被调用吗?

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo *demo = new ClassDemo; return ;
}

答案是:没有~!

5.2  改下代码

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo *demo = new ClassDemo; delete demo; return ;
}

运行:

调用带参数的:ClassDemo *demo = new ClassDemo(10);

5.3  加上命名空间的写法:PoEdu::ClassDemo *pdemo2 = new Poedu::ClassDemo();  //没有参数的写法

5.4  还可以简化成:PoEdu::ClassDemo *pdemo2 = new Poedu::ClassDemo ;  //简化后面的括号。


 6-->  定义对象数组

6.0  这里会调用多少个构造函数?是有参还是无参?答:调用10次无参的构造。

6.1  那么这个写法会调用几次?都调用了无参还是有参?分别几次?ClassDemo arry[10] = {1};  答:1次有参,9次无参。如图:

6.2  再看这个:ClassDemo *pArray = new ClassDemo[10];  答:调用10次无参。

 
 

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo *demo = static_cast<ClassDemo*>(malloc(sizeof(ClassDemo)));
free(demo); return ;
}

6.3  如上图,malloc()它不会调用构造函数,它只会开辟空间。所以生成一个对象,用new是准确的。同样,delete会调用析构函数,free()不会调用析构函数。

 

7-->  转换构造函数
 

7.0  看这个:ClassDemo demo = 10; 编译下,成功通过~!运行:

7.1  当C++看到  类  对象名  = 数值; 会调用转换构造函数。此时的=并不是赋值操作,它会调用构造函数。基于这种特性,我们将只有一个参数的构造函数称为:转换构造函数。

7.2  转换构造函数,就是只需要输入一个参数的函数。ClassDemo demo =10;  可以把它看成:ClassDemo demo(10);

7.3  赋值是这样:demo = 20;这里一定要与转换构造函数区别对待。这里调用的是“拷贝赋值函数”。

7.4  再看示例:ClassDemo.h代码如下

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
ClassDemo(int num,int other =100);
~ClassDemo(); //ClassDemo& operator=(const ClassDemo& other); int GetNum();
private:
int _num;
};
}
#endif //!_CLASSDEMO_H_

7.5  如果把cpp里面的代码改这样:

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} ClassDemo::ClassDemo(int num ,int other )
{
_num = num;
std::cout << "ClassDemo(" << num << ")" << std::endl;
} ClassDemo::~ClassDemo()
{
std::cout << "~ClassDemo( )" << std::endl;
} int ClassDemo::GetNum()
{
return _num;
}
}

运行:

7.6  说明:转换构造函数,它只需要一个参数正确的传递,只要能符合一个参数的赋值,它就能形成转换构造函数。只要符合ClassDemo demo(20);这样子语法的条件的函数,都可以形成转换构造函数。ClassDemo demo =20;这行代码的本质就是:ClassDemo demo(20);只是代码书写的方式稍有改变而已。它也是构造函数的一种。


8--> 拷贝赋值函数 

看代码:

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo demo = ; demo = ; //赋值函数
std::cout << demo.GetNum() << std::endl; return ;
}

断点,运行

8.1  ClassDemo& operator = (const ClassDemo& other);   operator操作符的意思。

8.2  新对象生成时,如果没有写构造函数,编译器会帮我们生成构造函数和析构函数,其实里面还有一系列默认函数被自动隐藏的生成了,当对象调用时,这些函数就被调用到,这里面就有一个很重要的函数:拷贝赋值函数。

8.3  ClassDemo& operator=(……);  这个是拷贝赋值函数 。上句代码不是重载,是默认生成的,拷贝赋值。这个函数里面做些什么事情呢?其实里面做的事情很简单,就是把每个参数进行拷贝。

代码:ClassDemo.h

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
ClassDemo(int num);
~ClassDemo(); ClassDemo& operator=(const ClassDemo& other);
int GetNum();
private:
int _num;
};
}
#endif //!_CLASSDEMO_H_

ClassDemo.cpp

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} ClassDemo::ClassDemo(int num )
{
_num = num;
std::cout << "ClassDemo(" << num << ")" << std::endl;
} ClassDemo::~ClassDemo()
{
std::cout << "~ClassDemo( " << _num << ")" << std::endl;
} ClassDemo& ClassDemo::operator=(const ClassDemo& other)
{
std::cout << "operator=(" <<_num << ")" << std::endl;//加上一句输出,便于查看。
_num = other._num;
return *this;
} int ClassDemo::GetNum()
{
return _num;
}
}

运行:

8.5  再细说demo =2;这行代码,2是一个int整数,赋值给demo,它们之间的类型是不一致的,那么,这个等号呢,C++给出了默认的修改:operator=(const ClassDemo& other);因为有了这个默认的修改,等号在这就是拷贝赋值函数,注意,“=”现在是函数了。

8.6  在这里,C++提供了一个隐式转换的机制,把2提升为:ClassDemo()2;这样子就符合了“=”函数operator=(ClassDemo(2))的调用条件。

8.7  注意,这里如果我们的一个int类型2,它没办法构成ClassDemo(2);类型,后面的隐式转换,参数的传递,就会不成立。

8.8  ClassDemo.h    注释了    转换构造函数

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
//ClassDemo(int num);
~ClassDemo(); ClassDemo& operator=(const ClassDemo& other); int GetNum();
private:
int _num;
};
}
#endif //!_CLASSDEMO_H_

ClassDemo.cpp   注释了  转换构造函数

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} /*ClassDemo::ClassDemo(int num )
{
_num = num;
std::cout << "ClassDemo(" << num << ")" << std::endl;
}*/ ClassDemo::~ClassDemo()
{
std::cout << "~ClassDemo( " << _num << ")" << std::endl;
} ClassDemo& ClassDemo::operator=(const ClassDemo& other)
{
std::cout << "operator=(" <<_num << ")" << std::endl;
_num = other._num;
return *this;
} int ClassDemo::GetNum()
{
return _num;
}
}

main.cpp

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo demo; demo = ; //赋值函数
std::cout << demo.GetNum() << std::endl; return ;
}

运行:没有了 转换构造函数,再看拷贝赋值函数的结果:

1>------ 已启动生成: 项目: PoEdu_MyClass, 配置: Release Win32 ------
1> main.cpp
1>main.cpp(10): error C2679: 二进制“=”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)
1> e:\c_code\poedu_myclass\poedu_myclass\ClassDemo.h(13): note: 可能是“PoEdu::ClassDemo &PoEdu::ClassDemo::operator =(const PoEdu::ClassDemo &)”
1> main.cpp(10): note: 尝试匹配参数列表“(PoEdu::ClassDemo, int)”时
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

8.9  编译器在这里,会先找默认的operator=,看operator=这个函数里面有没有能接收int类型的,如果没有,那么,再找后面的(const ClassDemo& other),看ClassDemo& other能不能匹配传一个int,也就是ClassDemo(int)这样子能不能成功。如果两次都失败,则编译器抛出错误异常。

8.10  可以得出,转换函数会使得 operator=得到实行。

8.11  是不是可以更改 operator=的参数为int型,让demo =2;这行代码编译通过呢?那是肯定的:

ClassDemo.h

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
//ClassDemo(int num);
~ClassDemo(); //ClassDemo& operator=(const ClassDemo& other);
ClassDemo& operator=(const int other); int GetNum();
private:
int _num;
};
}
#endif //!_CLASSDEMO_H_

ClassDemo.cpp

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} /*ClassDemo::ClassDemo(int num )
{
_num = num;
std::cout << "ClassDemo(" << num << ")" << std::endl;
}*/ ClassDemo::~ClassDemo()
{
std::cout << "~ClassDemo( " << _num << ")" << std::endl;
} ClassDemo& ClassDemo::operator=(const int other)
{
_num = other;
return *this;
} /*ClassDemo& ClassDemo::operator=(const ClassDemo& other)
{
std::cout << "operator=(" <<_num << ")" << std::endl;
_num = other._num;
return *this;
}*/ int ClassDemo::GetNum()
{
return _num;
}
}

main.cpp

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo demo; demo = ; //赋值函数
std::cout << demo.GetNum() << std::endl; return ;
}

运行:

8.12  其实这里的符号=,其本质是调用了某个带参数的函数。然而,现在此时,再来一个转换函数的代码:

8.13  反复的验证“=”加“参数类型”为一个函数。

8.14  还有一点,构造函数写了,就不自动默认生成,然而赋值函数却是,不管你写不写,始终都在~!一个构造函数都不写的时候,会生成默认构造函数。

8.15  下面验证一下:赋值函数,在手动写了以后,还会自动生成默认的拷贝赋值函数吗?

ClassDemo.cpp

#include "ClassDemo.h"
#include <iostream> namespace PoEdu
{
ClassDemo::ClassDemo()
{
std::cout << "ClassDemo()" << std::endl;
} ClassDemo::ClassDemo(int num )
{
_num = num;
std::cout << "ClassDemo(" << num << ")" << std::endl;
} ClassDemo::~ClassDemo()
{
std::cout << "~ClassDemo( " << _num << ")" << std::endl;
} ClassDemo& ClassDemo::operator=(const int other)
{
std::cout << "operator=int (" << _num << ")" << std::endl;
_num = other;
return *this;
} /*ClassDemo& ClassDemo::operator=(const ClassDemo& other)
{
std::cout << "operator=(" <<_num << ")" << std::endl;
_num = other._num;
return *this;
}*/ int ClassDemo::GetNum()
{
return _num;
}
}

ClassDemo.h

#ifndef _ClASSDEMO_H_
#define _CLASSDEMO_H_ namespace PoEdu
{
class ClassDemo
{
public:
ClassDemo();
ClassDemo(int num);
~ClassDemo(); //ClassDemo& operator=(const ClassDemo& other);
ClassDemo& operator=(const int other); int GetNum();
private:
int _num;
};
}
#endif //!_CLASSDEMO_H_

main.cpp

#include "ClassDemo.h"
#include <iostream> int main()
{
using namespace PoEdu; ClassDemo demo = ; demo = ; ClassDemo demo1();
demo = demo1; demo = ; //赋值函数
std::cout << demo.GetNum() << std::endl; return ;
}

运行:

8.16  看有没有调用默认的自动生成的赋值函数,当我有了一个手写的赋值函数的时候,看编译器还提不提供自动生成的默认赋值函数;

8.17  分析上例,一步步的调试,得出结论:不管你有没有写赋值函数,默认生成的赋值函数它都存在。它的做法就是帮我们做一个简单的赋值。赋值函数就相当于把“=”做了重载。

8.18  小结:这三行代码,很容易让人搞混语义 ,  1   ClassDemo demo = 1;     2   demo = 2;      3    demo = demo1;

8.19  记住了,第1个代码语义相当于:ClassDemo demo(1);   第2句和第3句都是拷贝赋值,“=”这里调用了拷贝赋值函数。他们之间的区别就是:第1它有创建一个新的对象。如果不想让第1种写法出现,那么,要怎么写呢,要加一个关键字:explicit


 9--> 总结

9.0    如果没有写任何一个构造函数,会生成默认(无需传参)的构造函数;如果写了任何一个构造,它都不再生成默认构造  还会生成一个默认的赋值函数,它接收的参数是当前类的对象引用;

9.1     只需要传递一相参数即可的构造 函数,会提升为转换构造函数,用于隐式转换;如:Test t = int; 这不是赋值,是转换构造函数。

9.2    默认生成拷贝赋值函数,不管你有没有手写一个,默认的赋值函数始终存在。

        


 10--> 初始化列表  :

10.0  好处:当一个变量为const时,初始化可以赋初值,还有当变量为引用时,初始化时就能给其赋值,不然就不能赋值了。


作业:做好这几天的笔记。

 

PoEdu- C++阶段班【Po学校】-Lesson03_构造函数精讲 - 第5天的更多相关文章

  1. PoEdu - C++阶段班【Po学校】- Lesson03-4_构造函数&赋值函数&拷贝构造函数&学习方式 - 第6天

    PoEdu - C++阶段班[Po学校]- 第6天 课堂选择题目: 1  关于转换构造函数  ClassDemo demo = 1;  调用转换构造函数 2  关于拷贝赋值函数  demo =2; 首 ...

  2. PoEdu - C++阶段班【Po学校】- 第3天

    引用 C中指针的功能强大,使用起来繁杂,因为指针要控制的东西太多:有指针的类型,指针的解引用,指针空间内的值,它本身是有空间的,有自己的地址等.指针也是强大的,比如:我们要在函数之内,修改方法之外的值 ...

  3. PoEdu - C++阶段班【Po学校】- Lesson02_类与对象_第4天

    复习:上节作业讲解 注意点: 设计SetString()的时候,要注意重置原来的空间. char * SetString(const char *str) { _len = strlen(str); ...

  4. PoEdu - C++阶段班【Po学校】- 第1课

    1 C++开讲 C ++  伟大的编程语言:能提高程序运行效率,节约更多的资源,"正确的使用C++,能够抑制全球变暖问题". 2 C++能力雷达图 通过 1效率 2灵活度 3 抽象 ...

  5. PoEdu - C++阶段班- Lesson07 To Lesson10_C to C++

    07  重载导致的二义性 问题:为什么一定要重载呢?重载能方便我们注重函数的功能,当参数类型不确定时,我们能很便捷的利用重载的机制达到目的. 重载注意点:二义性 看代码: #include <c ...

  6. PoEdu - C++阶段班- Lesson02_C to C++

    1  原生bool类型 c++里面的bool类型才是真正原生的true和faul,比如常见的大写的"BOOL",它就不是原生的. 原生的与非原生的bool,它们的区别: 详细说下原 ...

  7. PoEduo - C++阶段班【Po学校】-Lesson03-5_运算符重载- 第7天

    PoEduo - Lesson03-5_运算符重载- 第7天 复习前面的知识点 空类会自动生成哪些默认函数 6个默认函数    1  构造  2  析构   3  赋值  4 拷贝构造  5 oper ...

  8. 3.PO如何给开发团队讲好故事

    敏捷开发系列文章目录 讲出符合开发团队味口的故事. 上一章说了敏捷开发团队的构成与迭代过程,本章重点说一下迭代第一天的计划会议.熟话说“好的开始就成功了一半”,一个迭代的计划会议做得好不好确实直接注定 ...

  9. 清北学堂2018DP&图论精讲班 DP部分学习笔记

    Day 1 上午 讲的挺基础的--不过还是有些地方不太明白 例1 给定一个数n,求将n划分成若干个正整数的方案数. 例2 数字三角形 例7 最长不下降子序列 以上太过于基础,不做深入讨论 例3 给定一 ...

随机推荐

  1. js的原型模式

    以下内容来自<JavaScript高级程序设计>第三版 我们创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所 ...

  2. 40、dom以xml结尾的文件

    1.student.xml文件 <?xml version="1.0" encoding="utf-8" ?> <!-- 1.书写根元素(因为 ...

  3. php上传大文件设置方法

    打开php.ini,首先找到 ;;;;;;;;;;;;;;;; ; file uploads ; ;;;;;;;;;;;;;;;; 区域,有影响文件上传的以下几个参数: file_uploads = ...

  4. Highcharts 在低版本 IE 上使用注意事项

    来源:https://segmentfault.com/a/1190000004272693 很多人经常遇到图表在主流浏览器上运行正常,在低版本IE(包括IE6.IE7.IE8等)下运行出错(图表显示 ...

  5. sql server之数据库语句优化

    三.只返回需要的数据 返回数据到客户端至少需要数据库提取数据.网络传输数据.客户端接收数据以及客户端处理数据等环节,如果返回不需要的数据,就会增加服务器.网络和客户端的无效劳动,其害处是显而易见的,避 ...

  6. CentOS7安装问题及解决方案记录

    CentOS7系统已安装好: 一.我想要实现系统桌面化. 需要运行 yum 命令. 出现第一个error: 1.提示错误:can not find a valid baseurl 86_X64.... ...

  7. C#删除datable空行

    //去除dataTable空行 public DataTable RemoveEmpty(DataTable dt) { List<DataRow> removelist = new Li ...

  8. Android 无标题、全屏设置

    一.在主题中设置无标题.全屏 (一):直接设置主题: android:theme="@android:style/Theme.NoTitleBar.Fullscreen"  // ...

  9. Maven Archetype

    1. 从project创建archetype 在项目根目录下,运行 mvn archetype:create-from-project 创建的archetype工程在app_folder/target ...

  10. 【补充版】HashMap(根据value筛选查找)

    HashMap查找之根据Value查找 一般大家都知道对于HashMap而言都是通过key来进行查找.找到了key自然对应的value也就一并找到了.但是有些情况下就需要通过value来进行判断查找. ...