网站制作详细教程武冈企业建站

当前位置: 首页 > news >正文

网站制作详细教程,武冈企业建站,电子商务网站的类型,培训机构哪家最好一.什么是拷贝构造函数#xff1f; 1.1 概念 同一个类的对象在内存中有完全相同的结构#xff0c;如果作为一个整体进行复制或称拷贝是完全可行的。这个拷贝过程只需要拷贝数据成员#xff0c;而函数成员是共用的#xff08;只有一份拷贝#xff09;。 在建立对象…一.什么是拷贝构造函数 1.1 概念 同一个类的对象在内存中有完全相同的结构如果作为一个整体进行复制或称拷贝是完全可行的。这个拷贝过程只需要拷贝数据成员而函数成员是共用的只有一份拷贝。         在建立对象时可用同一类的另一个对象来初始化该对象这时所用的构造函数称为拷贝构造函数 Copy Constructor。 拷贝构造函数的参数必须采用引用类型但并不限制为const一般普遍的会加上const限制。如果以类对象作为参数传递到拷贝构造函数会引起无穷递归。 1.2 代码示例 代码示例如下 #include iostreamusing namespace std;class CStudent { public:CStudent(int age 0,int score 0);~CStudent();//拷贝构造函数 CStudent(const CStudent stu);private:int age;int score; };CStudent::CStudent(int age,int score) {coutConstructor!endl;this-age age;this-score score; }CStudent::~CStudent() {coutDesconstructor!endl; }CStudent::CStudent(const CStudent stu) {coutCopy constuctor!endl;this-age stu.age;this-score stu.score; } 二.如何实现 2.1 缺省拷贝构造函数 2.1.1 概念 如果类中没有给出定义系统会自动提供缺省拷贝构造函数。 缺省的拷贝构造函数会按成员语义依次拷贝每个类成员亦称为缺省的按成员初始化。 按成员作拷贝是通过依次拷贝每个数据成员实现的而不是对整个类对象按位拷贝。 2.1.2 代码示例 示例代码如下 #include iostreamusing namespace std;class CStudent { public:CStudent(int age 0,int score 0);~CStudent();void print_info(void);private:int age;int score; };CStudent::CStudent(int age,int score) {coutConstructor! thisendl;this-age age;this-score score; }CStudent::~CStudent() {coutDesconstructor! thisendl; }void CStudent::print_info(void) {coutage(this): ageendl;coutscore(this): scoreendl; }int main(int argc, char** argv) {CStudent stu1(8,90);CStudent stu2(stu1);stu2.print_info();return 0; } 运行结果如下图所示。        由上图可知 1只调用了一次普通构造函数用来构造对象stu1。表明在构造stu2时调用了一个缺省的构造函数这个函数就是拷贝构造函数。 2对象stu2的所有数据成员被初始化为stu1对应数据成员的值。 3最后调用了两次析构函数用于析构stu1和stu2。 2.2 自定义拷贝构造函数 2.2.1 概念 通常按成员语义支持已经足够。但在某些情况下它对类与对象的安全性和处理的正确性还不够这时就要求类的设计者提供特殊的拷贝构造函数定义。 2.2.2 代码示例 示例代码如下 #include iostreamusing namespace std;class CStudent { public:CStudent(int age 0,int score 0);~CStudent();//拷贝构造函数 CStudent(const CStudent stu);void print_info(void); private:int age;int score; };CStudent::CStudent(int age,int score) {coutConstructor!endl;this-age age;this-score score; }CStudent::~CStudent() {coutDesconstructor!endl; }CStudent::CStudent(const CStudent stu) {coutCopy constuctor!endl;this-age stu.age;this-score stu.score; }void CStudent::print_info(void) {coutage: ageendl;coutscore: scoreendl; }int main(int argc, char** argv) {CStudent stu1(8,90);CStudent stu2(stu1);stu2.print_info();return 0; } 运行结果如下图所示。 由上图可知 1构造stu2对象时调用了一次自定义的拷贝构造函数。 2关注一下自定义构造函数代码发现在函数域内可通过引用对象访问私有数据成员age和score。 从逻辑上讲每个对象有自己的成员函数访问同类其他对象的私有数据成员应通过该对象的公有函数不能直接访问。但在物理上只有一个成员函数拷贝所以直接访问是合理的。 即C有个原则类的成员函数可以访问私有数据成员。 CStudent::CStudent(const CStudent stu) {coutCopy constuctor!endl;this-age stu.age;this-score stu.score; } 三.何时调用 3.1 用对象初始化对象 以下两种形式都是用已存在的对象初始化对象 CStudent stu1(8,90);CStudent stu2(stu1); 或者 CStudent stu2 stu1; 以上两种形式是等价的只是写法上不同。 3.2 给函数传递类的对象参数 当函数的形参是类的对象时 一旦调用函数要在内存新建立一个局部对象并把实参拷贝到新的对象中。 代码示例部分如下 void func(CStudent stu) {coutfuncendl; }int main(int argc, char** argv) {CStudent stu1(8,90);func(stu1);return 0; } 运行结果如下图所示。 由上图可知。调用func函数时会调用拷贝构造函数构造一个临时对象传给func。 3.3 函数返回类的对象部分编译器 很多资料提到如果函数的返回值是类的对象那么函数执行完成后返回调用者时会调用拷贝构造函数。其实这不严谨。 有些编译器在函数返回类的对象时不会调用拷贝构造函数。下面单独一节详细分析。 四.函数返回类的对象但不调用拷贝构造函数 本次实验使用64位TDM-GCC 4.9.2编译器。 4.1 示例代码         #include iostreamusing namespace std;class CStudent { public:CStudent(int age 0,int score 0);~CStudent();//拷贝构造函数 CStudent(const CStudent stu);void print_info(void); private:int age;int score; };CStudent::CStudent(int age,int score) {coutConstructor!endl;this-age age;this-score score; }CStudent::~CStudent() {coutDesconstructor!endl; }CStudent::CStudent(const CStudent stu) {coutCopy constuctor!endl;this-age stu.age;this-score stu.score; }void CStudent::print_info(void) {coutage: ageendl;coutscore: scoreendl; }CStudent func(void) {CStudent tmp(11,88);return tmp; }int main(int argc, char** argv) {CStudent stu1(8,90);CStudent stu2;stu2 func();stu2.print_info();return 0; } 4.2 运行结果 如下图所示。 由下图可知 1func函数的返回值是类的对象但并没有调用拷贝构造函数。 2从stu2打印的信息来看func函数中创建的tmp对象的确“赋值”给了stu2。这怎么理解下面看看汇编代码。 4.3 汇编代码 汇编代码中r8d是指r8寄存器的低32位。 4.3.1 func函数汇编代码     完整的汇编代码如下 push %rbp mov %rsp,%rbp sub \(0x20,%rsp mov %rcx,0x10(%rbp) //rcx存储了对象tmp的地址 mov \)0x58,%r8d //r8d的低32位初始化为88 mov \(0xb,%edx //edx初始化为11 mov 0x10(%rbp),%rcx //即是tmp对象地址 callq 0x401530 CStudent::CStudent(int, int) nop mov 0x10(%rbp),%rax add \)0x20,%rsp pop %rbp retq 如上图中的注释func函数里的对象tmp的地址是由调用者main函数传入的即tmp对象是在main函数的堆栈里存储而不是在func函数的堆栈里。 4.3.2 构造函数汇编代码 CStudent::CStudent(int, int)函数的完整汇编代码如下 push %rbp mov %rsp,%rbp sub \(0x20,%rsp mov %rcx,0x10(%rbp)//rcx存储了对象tmp的地址 mov %edx,0x18(%rbp) //初始化tmp.score的值为11 mov %r8d,0x20(%rbp) //初始化tmp.age的值为88 lea 0x86ab6(%rip),%rdx # 0x488000 mov 0x8b17f(%rip),%rcx # 0x48c6d0 .refptr._ZSt4cout callq 0x46ee10 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc mov 0x8b183(%rip),%rdx # 0x48c6e0 .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ mov %rax,%rcx callq 0x44d500 _ZNSolsEPFRSoS_E mov 0x10(%rbp),%rax mov 0x18(%rbp),%edx mov %edx,(%rax) mov 0x10(%rbp),%rax mov 0x20(%rbp),%edx mov %edx,0x4(%rax) add \)0x20,%rsp pop %rbp retq
retq 注意第4~5行代码的注释。构造函数里初始化了tmp对象的数据成员。 4.3.3 main函数汇编代码 main函数的完整汇编代码如下 push %rbp push %rbx sub \(0x58,%rsp lea 0x80(%rsp),%rbp mov %ecx,-0x10(%rbp) mov %rdx,-0x8(%rbp) callq 0x40e950 __main lea -0x50(%rbp),%rax //堆栈偏移0x50的空间分配给对象stu1.这里rax存储了stu1的地址 mov \)0x5a,%r8d //r8的低32位初始化为90 mov \(0x8,%edx //edx寄存器初始化为8 mov %rax,%rcx //传递stu1的地址给构造函数 callq 0x401530 CStudent::CStudent(int, int) lea -0x60(%rbp),%rax //堆栈偏移0x60的空间分配给对象stu2.这里rax存储了stu2的地址 mov \)0x0,%r8d mov \(0x0,%edx mov %rax,%rcx //传递stu2的地址给构造函数 callq 0x401530 CStudent::CStudent(int, int) lea -0x40(%rbp),%rax //堆栈偏移0x40的空间分配给了一个临时对象暂时命名为m_tmp.这里rax存储了m_tmp的地址 mov %rax,%rcx //传递m_tmp的地址给func函数 callq 0x401685 func() //func函数里的tmp对象直接使用了main函数创建的m_tmp mov -0x40(%rbp),%rax mov %rax,-0x60(%rbp) //将m_tmp赋值给stu2 lea -0x40(%rbp),%rax mov %rax,%rcx callq 0x40157e CStudent::~CStudent() //析构m_tmp lea -0x60(%rbp),%rax mov %rax,%rcx callq 0x401606 CStudent::print_info() mov \)0x0,%ebx lea -0x60(%rbp),%rax mov %rax,%rcx callq 0x40157e CStudent::~CStudent() lea -0x50(%rbp),%rax mov %rax,%rcx callq 0x40157e CStudent::~CStudent() mov %ebx,%eax jmp 0x401770 main(int, char)192 mov %rax,%rbx lea -0x60(%rbp),%rax mov %rax,%rcx callq 0x40157e CStudent::~CStudent() jmp 0x401759 main(int, char)169 mov %rax,%rbx lea -0x50(%rbp),%rax mov %rax,%rcx callq 0x40157e CStudent::~CStudent() mov %rbx,%rax mov %rax,%rcx callq 0x40f670 _Unwind_Resume add $0x58,%rsp pop %rbx pop %rbp retq 如代码中的注释 1main函数在调用func函数前创建了一个临时对象这里给它命名为m_tmp。 2m_tmp对象的地址传递给func函数func函数里的tmp对象直接使用了m_tmp的地址。因此可以认为tmp就是m_tmp的别名。 3func函数返回后将m_tmp对象的数据赋值给stu2对象。 4最后析构m_tmp。 所以从始至终没有调用过拷贝构造函数。