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. 使用英文版eclipse保存代码,出现some characters cannot be mapped using "Cp1251" character encoding.

    some characters cannot be mapped using "Cp1251" character encoding. 解决办法:方案一: eclipse-> ...

  2. Spark的数据存储

    Spark本身是基于内存计算的架构,数据的存储也主要分为内存和磁盘两个路径.Spark本身则根据存储位置.是否可序列化和副本数目这几个要素将数据存储分为多种存储级别.此外还可选择使用Tachyon来管 ...

  3. java 中 ArrayList LinkedList Vector 三者的异同点

    1.ArrayList和Vector都是基于数组实现的,所以查询速度很快,增加和删除(非最后一个节点)速度慢: Vector是线程安全的,ArrayList不是. 2.LinkedList 是一个双向 ...

  4. C# string.Split对于换行符的分隔正确用法

    C# string.Split对于换行符的分隔正确用法 tmpCase "11117144-8c91-4817-9b92-99ec2f9d784a\r\n23D95A26-012C-4332 ...

  5. Python: 元组的基本用法

    元组和列表是近亲,列表允许并且提供了方法来改变它的值,但元组是不可改变的,即不允许你改变它的值----这也是它没有方法的部分原因. 元组的主要作用是作为参数传递给函数调用.或是从函数调用那里获得参数时 ...

  6. sort详解2

    linux sort 命令详解 sort是在Linux里非常常用的一个命令,管排序的,集中精力,五分钟搞定sort,现在开始! 1 sort的工作原理 sort将文件的每一行作为一个单位,相互比较,比 ...

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

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

  8. c#窗体去掉borderstyle进行拖动

    private void BaseCForm_Load(object sender, EventArgs e)        {            DrawStyle(); this.panel1 ...

  9. adb opendir failed ,permission denied

    做数据库的时候,我手机是htc的,root过的,找数据库db文件一直找不到, 我使用的adb命令ls的时候会提示:adb opendir failed ,permission denied ,解决方法 ...

  10. Python-day-21

    1.请求周期 url> 路由 > 函数或类 > 返回字符串或者模板语言? Form表单提交: 提交 -> url > 函数或类中的方法 - .... HttpRespon ...