一、explicit构造函数显示调用声明,指定这个构造器只能被明确地调用/使用,不能作为类型转换操作符被隐含地使用:
class Test1 { public: Test1(int n) { num=n; }//普通构造函数 private: int num; }; class Test2 { public: explicit Test2(int n) { num=n; }//explicit(显式)构造函数 private: int num; }; int main() { Test1 t1=12;//隐式调用其构造函数,成功 Test2 t2=12;//编译错误,不能隐式调用其构造函数 Test2 t2(12);//显式调用成功 return 0; }
二、复制构造函数
浅复制:是指在复制类的对象时,将复制其指针成员,但不复制指针指向的缓存区,其结果是两个对象指向同一块动态分配的内存。销毁其中一个对象时,delete[]释放这个内存块,导致另一个对象复制的指针拷贝无效。
#include <iostream> #include <string.h> using namespace std; class MyString { private: char* buffer; public: MyString(const char* initString) { cout << "Invoking costructor." << endl; buffer = NULL; if (initString != NULL) { buffer = new char[strlen(initString) + 1]; strcpy(buffer, initString); } } ~MyString() { cout << "Invoking destructor, clear up." << endl; delete[] buffer; } int GetLength() { return strlen(buffer); } const char* GetString() { return buffer; } }; void UseMyString(MyString str) { cout << "String buffer in MyString is: " << str.GetString() << endl; return; } int main() { MyString sayHello("Hello from String Class"); UseMyString(sayHello); system("pause"); return 0; }
运行以后引起崩溃,因为在main()中,将使用MyString对象sayHello的工作交给了函数UseMyString(),则sayHello被复制到了形参str,并在UseMyString()中使用它。编译器之所以进行复制,是因为函数UseMyString()的参数str被声明为按值(而不是按引用)传递。对于整型、字符和原始指针等POD数据,编译器执行二进制复制,因此sayHello.buffer包含的指针值被复制到了str中,即sayHello.buffer和str.buffer指向同一个内存单元。当函数UseMyString()返回时,变量str不再在作用域内,因此被销毁。此时将调用MyString类的析构函数,而该析构函数使用delete[]释放分类给buffer的内存。等到main()执行完毕,sayHello对象将不再在作用域被销毁时,此时sayHello.buffer的内存已经被销毁过一次,重复调用delete导致程序崩溃。
指针传递或引用传递,都不存在形参的浅复制问题。
void UseMyString(MyString* str) { cout << "String buffer in MyString is: " << str->GetString() << endl; return; } int main() { MyString sayHello("Hello from String Class"); UseMyString(&sayHello); system("pause"); return 0; }
void UseMyString(MyString& str) { cout << "String buffer in MyString is: " << str.GetString() << endl; return; } int main() { MyString sayHello("Hello from String Class"); UseMyString(sayHello); system("pause"); return 0; }
编译器提供的默认复制赋值运算符operator=将导致浅复制。
int main() { MyString sayHello("Hello from String Class"); //UseMyString(sayHello); MyString say2 = sayHello; system("pause"); return 0; }
此时main()结束时,会调用两次析构函数,分别是sayHello和say2的,say2会引起析构函数重复delete已经释放的内存。
#include <iostream> #include <string.h> using namespace std; class MyString { private: char* buffer; public: MyString(const char* initString) { cout << "Invoking costructor." << endl; buffer = NULL; if (initString != NULL) { buffer = new char[strlen(initString) + 1]; strcpy(buffer, initString); } } MyString(const MyString& copySource) // 复制函数 { buffer = NULL; cout << "Copy costructor." << endl; if (copySource.buffer != NULL) { buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); // 深复制 } cout << "buffer points to : 0x" << hex << (unsigned int*)buffer << endl; } ~MyString() { cout << "Invoking destructor, clear up." << endl; delete[] buffer; } int GetLength() { return strlen(buffer); } const char* GetString() { return buffer; } }; void UseMyString(MyString str) { cout << "String buffer in MyString is: " << str.GetString() << endl; return; } int main() { MyString sayHello("Hello from String Class"); cout << "======start UseMyString..." << endl; UseMyString(sayHello); cout << "=====start use operator= ..." << endl; MyString say2 = sayHello; system("pause"); return 0; }
使用复制构造函数,确保变量进行深复制,复制构造函数由程序员自己编写。注意:
1)通过在复制构造函数中使用const,可确保复制构造函数不会修改指向的源对象。
2)复制构造函数的参数必须按引用传递,否则复制构造函数将不断调用自己,直到耗尽系统内存;
3)如果MyString类中不包含原始指针成员,如不用char*,使用std::string;不用int*,使用std::vector<int>等。那么不需要编写复制构造函数。因为编译器添加的默认复制构造函数将调用成员对象(如std::string)的复制构造函数。
三,移动构造函数
移动构造函数通常是利用C++11的移动赋值运算符实现。使用复制构造函数,虽然避免了浅复制,但深复制大对象又可能造成性能压力。如果对象A赋值给对象B以后,A不再使用,则可以使用移动构造函数。
#include <iostream> #include <string.h> using namespace std; class MyString { private: char* buffer; public: MyString(const char* initString) { cout << "Invoking costructor. 0x" << hex << (unsigned int*)this << endl; buffer = NULL; if (initString != NULL) { buffer = new char[strlen(initString) + 1]; strcpy(buffer, initString); } } MyString(const MyString& copySource) // 复制函数 { buffer = NULL; cout << "Copy costructor. 0x" << hex << (unsigned int*)this << endl; if (copySource.buffer != NULL) { buffer = new char[strlen(copySource.buffer) + 1]; strcpy(buffer, copySource.buffer); // 深复制 } //cout << "buffer points to : 0x" << hex << (unsigned int*)buffer << endl; } MyString(MyString&& moveSource) // 移动构造函数 { cout << "Move costructor. 0x" << hex << (unsigned int*)this << endl; if (moveSource.buffer != NULL) { buffer = moveSource.buffer; moveSource.buffer = NULL; } } ~MyString() { cout << "Invoking destructor, clear up. 0x" << hex << (unsigned int*)this << endl; delete[] buffer; } int GetLength() { return strlen(buffer); } const char* GetString() { return buffer; } }; void UseMyString(MyString str) { cout << "----String buffer in MyString is: " << str.GetString() << endl; return; } MyString getMyString() { MyString say("new test..."); cout << "----new say buffer is: " << say.GetString() << endl; return say; } void test() { MyString sayHello("Hello from String Class"); cout << "======copy1 construct..." << endl; UseMyString(sayHello); // 触发复制构造函数的第一种方式 cout << endl; cout << "======copy2 construct..." << endl; MyString copySay2(sayHello); // 触发复制构造函数的第二种方式式 cout << endl; cout << "======copy3 construct..." << endl; MyString copySay3 = sayHello; // 触发复制构造函数的第三种方式 cout << endl; cout << "======moveSay1 construct..." << endl; MyString moveSay1 = getMyString(); // 触发移动构造函数(注意,取决于编译器,qt5.8 linux触发构造函数) cout << endl; cout << "======moveSay2 construct..." << endl; MyString say3(std::move(sayHello)); // 触发移动构造函数 cout << endl; if (sayHello.GetString() == NULL) { cout << "sayHello.GetString is NULL ..." << endl; } } int main() { test(); system("pause"); return 0; }
Invoking costructor. 0x004FFAD4 ======copy1 construct... Copy costructor. 0x004FF9BC ----String buffer in MyString is: Hello from String Class Invoking destructor, clear up. 0x004FF9BC ======copy2 construct... Copy costructor. 0x004FFAC8 ======copy3 construct... Copy costructor. 0x004FFABC ======moveSay1 construct... Invoking costructor. 0x004FF9A0 ----new say buffer is: new test... Move costructor. 0x004FFAB0 Invoking destructor, clear up. 0x004FF9A0 ======moveSay2 construct... Move costructor. 0x004FFAA4 sayHello.GetString is NULL ... Invoking destructor, clear up. 0x004FFAA4 Invoking destructor, clear up. 0x004FFAB0 Invoking destructor, clear up. 0x004FFABC Invoking destructor, clear up. 0x004FFAC8 Invoking destructor, clear up. 0x004FFAD4 请按任意键继续. . .
四、重载operator=
注意几种方式的区别:
MyString copySay3 = sayHello;// 触发复制构造函数,不触发构造函数
MyString copySay3;// 触发构造函数 copySay3 = sayHello; // 触发operator=
重载方式:
MyString& operator=(const MyString& str) { cout << "operator=. 0x" << hex << (unsigned int*)this << endl; return *this; }
禁止operator=赋值:
private: MyString& operator=(const MyString& str);
五、单例模式
class MyString { private: MyString(); MyString& operator=(const MyString& str); char* buffer; public: static MyString& getInstance() { static MyString instance; // 只创建一次实例 return instance; } };
六、类的实例化区别
#include <iostream> #include <string.h> using namespace std; class MyString { private: MyString& operator=(const MyString& str); public: MyString() { cout << "Invoking costructor. 0x" << hex << (unsigned int*)this << endl; } ~MyString() { cout << "Invoking destructor, clear up. 0x" << hex << (unsigned int*)this << endl; } }; void test1() { MyString str; // 栈空间申请内存,不用手动释放 } void test2() { MyString* str = new MyString(); // 自由存储区申请动态内存 delete str; // 必须delete才能触发析构函数,释放动态内存 } int main() { cout << "test1.........." << endl; test1(); cout << "---------------" << endl; cout << "test2.........." << endl; test2(); cout << "---------------" << endl; system("pause"); return 0; }
七、私有化析构函数
私有化析构函数将导致无法在栈空间申请类实例。例如编写一个数据库类,其内部结构包含数TB数据,而栈空间通常有限,可能应该禁止在栈上实例化,只允许在自由存储区中创建实例。
由于析构函数私有化,不可调用,需要手工创建静态函数进行释放。
#include <iostream> #include <string.h> using namespace std; class MyString { private: ~MyString() { cout << "Invoking destructor, clear up. 0x" << hex << (unsigned int*)this << endl; } public: MyString() { cout << "Invoking costructor. 0x" << hex << (unsigned int*)this << endl; } static void DestroyInstance(MyString* instance) { cout << "DestroyInstance. 0x" << hex << (unsigned int*)instance << endl; delete instance; } }; void test1() { //MyString str; // 报错:无法实例化 } void test2() { MyString* str = new MyString(); // 自由存储区申请动态内存 //delete str; // 报错:无法触发析构函数 MyString::DestroyInstance(str); // 必须手工释放 } int main() { cout << "test1.........." << endl; test1(); cout << "---------------" << endl; cout << "test2.........." << endl; test2(); cout << "---------------" << endl; system("pause"); return 0; }
私有化构造函数,将无法以任何方式创建实例。而私有化析构函数,只是不允许在栈空间创建实例。
八,友元声明,访问私有成员和方法:
#include <iostream> #include <string.h> using namespace std; class MyString { private: int age; friend void printAge(MyString&); // 告诉编译器,该全局函数为友元函数,允许该函数内访问私有变量 friend class Utility; // 告诉编译器,该类为友元类,允许该类的函数访问私有变量 public: MyString(int a):age(a) { cout << "Invoking costructor. 0x" << hex << (unsigned int*)this << endl; } ~MyString() { cout << "Invoking destructor, clear up. 0x" << hex << (unsigned int*)this << endl; } }; void printAge(MyString& item) { cout << "printAge private member age is :" << dec << item.age << endl; // dec用于输出十进制,hex用于输出十六进制 } class Utility { public: void printMyStringAge(MyString& item) { cout << "printMyStringAge private member age is :" << dec << item.age << endl; } }; void test1() { MyString str(10); // 报错:无法实例化 printAge(str); Utility u; u.printMyStringAge(str); } int main() { cout << "test1.........." << endl; test1(); cout << "---------------" << endl; system("pause"); return 0; }
九,对类和结构使用聚合初始化
满足以下条件的类或结构体作为聚合类型,可以作为一个整体进行初始化:
1)只包含公有和非静态数据成员,不包含私有或受保护的数据成员;
2)不包含任何虚成员函数;
3)只涉及公有继承(不涉及私有、受保护和虚拟继承);
4)不包含用户定义的结构函数。
#include <iostream> #include <string.h> using namespace std; class MyClass { public: char* name; int age; string intro; char number[5]; void print() { cout << "print:" << name << endl; } }; struct MyStruct{ char* name; int age; string intro; char number[5]; void print() { cout << "print:" << name << endl; } }; union MyUnion { char* name; int age; }; int main() { MyClass myClass{ "name1", 10, "myClass...", {'1', '2', '3'} }; myClass.print(); MyStruct myStruct{ "name2", 10, "MyStruct...",{ '1', '2', '3' } }; myStruct.print(); MyUnion myUnion{"name3"}; cout << myUnion.name << endl; cout << sizeof(MyUnion) << endl; // 4 cout << sizeof(myUnion) << endl; // 4 system("pause"); return 0; }
十,在调用基类的函数
#include <iostream> #include <string.h> using namespace std; class MyBase { public: char* name; int age; string intro; char number[5]; MyBase(const char* n) { name = new char[strlen(n) + 1]; strcpy(name, n); } void print() { cout << "print MyBase:" << name << endl; } }; class MySub: public MyBase { public: MySub(const char* n) :MyBase(n) { }; void print() { MyBase::print(); // 访问基类,可以不用声明public继承 cout << "print MySub:" << name << endl; } }; int main() { MySub item("test"); item.print(); item.MyBase::print(); // 必须继承时声明public,如果只是class MySub: MyBase则不允许访问 system("pause"); return 0; }
十一,使用final禁止继承
从C++11起,编译器支持限定符final。被声明final的类不能用作基类。
class MyClass final { public: };
MyClass类禁止被继承。
十二、使用virtual虚函数实现多态
#include <iostream> #include <string.h> using namespace std; class MyBase { public: char* name; MyBase(const char* n) { name = new char[strlen(n) + 1]; strcpy(name, n); } void print() { cout << "print MyBase:" << name << endl; } virtual void virtualPrint() { cout << "myTest MyBase:" << name << endl; } }; class MySub : public MyBase { public: MySub(const char* n) :MyBase(n) { }; void print() { cout << "print MySub:" << name << endl; } void virtualPrint() { cout << "myTest MySub:" << name << endl; } }; void testPrint(MyBase& item) { item.print(); // 由引用的实际类来调用函数 } void testVirtualPrint(MyBase& item) { item.virtualPrint(); // 只调用子类的函数 } int main() { MyBase base = ""; testPrint(base); cout << "----------------------" << endl; MySub sub(""); testPrint(sub); cout << "----------------------" << endl; testVirtualPrint(sub); cout << "----------------------" << endl; system("pause"); return 0; }
十三、虚函数基类析构函数的作用,避免内存泄漏
在自由存储空间new申请子类实例,delete时如果释放父类指针,将无法触发子类析构函数,从而内存泄漏。此时需要将父类析构函数声明为虚函数。
#include <iostream> #include <string.h> using namespace std; class MyBase { public: MyBase() { cout << "MyBase construct..." << endl; } ~MyBase() { cout << "MyBase destruct..." << endl; } void print() { cout << "print MyBase..." << endl; } virtual void virtualPrint() { cout << "myTest MyBase..." << endl; } }; class MySub : public MyBase { public: MySub() { cout << "MySub construct..." << endl; } ~MySub() { cout << "MySub destruct..." << endl; } void print() { cout << "print MySub..." << endl; } void virtualPrint() { cout << "myTest MySub..." << endl; } }; void testPrint(MyBase& item) { item.print(); // 由引用的实际类来调用函数 } void testVirtualPrint(MyBase& item) { item.virtualPrint(); // 只调用子类的函数 } void test1() { MySub sub; // 栈上申请的空间,会自动调用子类析构函数 } void destoryBase(MyBase* item) { delete item; } void test2() { MySub* sub = new MySub(); destoryBase(sub); // 此时不会调用MySub的析构函数,除非MyBase的析构函数声明为virtual } void destorySub(MySub* item) { delete item; } void test3() { MySub* sub = new MySub(); destorySub(sub); // 会调用MySub的析构函数 } int main() { test1(); cout << "---------------" << endl; test2(); cout << "---------------" << endl; test3(); system("pause"); return 0; }
输出:
MyBase construct... MySub construct... MySub destruct... MyBase destruct... --------------- MyBase construct... MySub construct... MyBase destruct... --------------- MyBase construct... MySub construct... MySub destruct... MyBase destruct... 请按任意键继续. . .
父类析构函数务必声明为virtual,避免内存泄漏:
virtual~MyBase() { cout << "MyBase destruct..." << endl; }
十四,纯虚函数
virtual void pureVirtual() = 0; // 纯虚函数,子类必须实现
声明纯虚函数的父类,不允许被实例化,且子类必须实现该纯虚函数。
十五,虚继承
当多父类继承时,如果父类的上一级父类为同一个,则会造成多次实例化顶级父类。
#include <iostream> #include <string.h> using namespace std; class MyBase { public: MyBase() { cout << "MyBase construct..." << endl; } }; class MySub1 : public MyBase { public: MySub1() { cout << "MySub1 construct..." << endl; } }; class MySub2 : public MyBase { public: MySub2() { cout << "MySub2 construct..." << endl; } }; class MySubSub : public MySub1, public MySub2 { public: MySubSub() { cout << "MySubSub construct..." << endl; } }; int main() { MySubSub subsub; system("pause"); return 0; }
输出:
MyBase construct... MySub1 construct... MyBase construct... MySub2 construct... MySubSub construct... 请按任意键继续. . .
此时只有使用虚继承来避免:
#include <iostream> #include <string.h> using namespace std; class MyBase { public: MyBase() { cout << "MyBase construct..." << endl; } }; class MySub1 : public virtual MyBase { public: MySub1() { cout << "MySub1 construct..." << endl; } }; class MySub2 : public virtual MyBase { public: MySub2() { cout << "MySub2 construct..." << endl; } }; class MySubSub : public MySub1, public MySub2 { public: MySubSub() { cout << "MySubSub construct..." << endl; } }; int main() { MySubSub subsub; system("pause"); return 0; }
输出:
MyBase construct... MySub1 construct... MySub2 construct... MySubSub construct... 请按任意键继续. . .
十六,关键字virtual两个不同的概念:
1)在函数声明中,当父类指针指向子类对象时,通过它可以调用子类的函数;
2)在类继承中声明,如从Base类派生出Derived1和Derived2两个子类,使用了关键字virtual,则意味着当Sub同时继承Derived1和Derived2时,Sub实例只包含一个Base实例。
十七,类继承时的public/private声明
继承时可声明public/private,不声明默认private。区别在于该子类的对象实例,是否可以直接访问父类中,声明public的公有成员变量和公有函数。
#include <iostream> #include <string> using namespace std; class Base { private: string privateName = "privateName"; protected: string protectedName = "protectedName"; public: string publicName = "publicName"; }; class Sub : public Base { public: void print() { //cout << privateName << endl; // error cout << protectedName << endl; // ok,不受public继承影响 cout << publicName << endl; // ok,不受public继承影响 } }; int main() { Sub sub; sub.print(); sub.publicName; // 只有public继承时可以访问 system("pause"); return 0; }
十八,const与函数
1,const放在函数后:
1)该函数只能被const对象实例访问;
2)该函数不允许修改类的成员变量;
2,const放在函数前:
函数返回的值是const的。
#include <iostream> #include <string.h> using namespace std; class MyBase { private: int age = 10; public: MyBase() { cout << "MyBase construct..." << endl; } int getAgeConst() const { //age = 20; // error return age; } int getAge() { age = 20; return age; } }; int main() { MyBase base; cout << base.getAge() << endl; cout << base.getAgeConst() << endl; const MyBase baseConst; //cout << baseConst.getAge() << endl; // error cout << baseConst.getAgeConst() << endl; system("pause"); return 0; }
十九,const与指针
见《C++11笔记一》十二
二十,const与变量
该变量的值不允许改变;
相关推荐
C++学习笔记C++学习笔记C++学习笔记C++学习笔记C++学习笔记
C++学习笔记
### C++ 学习笔记精华版 #### 一、C++ 语言概述 **1、历史背景** - **C++ 的江湖地位** - Java、C、C++、Python、C# 是当前主流的编程语言之一,而 C++ 在这些语言中以其高效性和灵活性著称。 - **C++ 之父 ...
2. C++中的构造函数有几种类型:默认构造函数、拷贝构造函数、委托构造函数等。构造函数是在创建对象时自动调用的特殊成员函数,用于初始化对象的成员变量。 3. 结构体内存对齐是C++中为了提高内存存取效率而采取的...
《千锋C++笔记》是一份综合性的学习资料,涵盖了C++编程语言的基础到高级概念。这份笔记由知名教育机构“千锋”提供,旨在帮助初学者和有一定基础的程序员深入理解和掌握C++这一强大的系统级编程语言。下面将详细...
6. **C++11及以后的新特性**:C++11引入了许多新特性,如auto关键字、右值引用、lambda表达式、智能指针等,C++14和C++17进一步增强了这些特性,使得C++更加现代化和高效。 7. **内存管理**:理解指针是学习C++的...
C++核心学习笔记pdf
【C++课程笔记详解】 达内的C++课程是专为深入学习编程技术的学员设计的,特别是对于希望在计算机科学领域提升技能或者从事软件开发工作的人来说,这是一个宝贵的资源。本笔记涵盖的内容广泛,旨在帮助学生全面理解...
这份"C++笔记"包含了学习C++时的重要知识点和实践技巧。 1. **基础语法**:C++的基础包括变量、数据类型(如整型、浮点型、字符型等)、运算符(算术、比较、逻辑、位运算符等)、流程控制语句(如if-else、switch-...
"自考C++笔记(上)" 本笔记是作者全部手打创作的自考C++笔记,包含课本中例子的详细分析,共47200字,适合没有学过C语言的人认真学习和通过C++自考。 C++程序设计 ### 认识 C++的对象 #### 1.1 初识 ...
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
这份笔记主要涵盖了C++的基础知识,包括C++11和C++17的新特性,旨在帮助初学者理解C++的核心概念。 ### C++11特性 1. **引用(Reference)**:引用是一种别名机制,一旦引用被初始化为一个对象后,它就始终绑定到...
今天,让我们一起深入探究《黑马C++学习笔记》,这本涵盖了C++编程基础与核心概念的全面教材。 首先,C++的基础知识是每个学习者必须掌握的部分,这包括变量的声明与赋值、输出语句等基本操作。在C++中,变量的声明...
从给出的部分内容来看,读书笔记主要聚焦于以下几个知识点: 1. C++语言的联邦概念:C++是一个由多个次语言构成的语言联邦,这包括了C语言核心、面向对象的C++、模板C++以及标准模板库(STL)。这种理解对于深入...
2. **运行安装程序**:挂载成功后,找到并运行安装程序(setup.exe),按照提示完成安装过程。 ### 数据类型及初始化 在C++中,数据类型是程序设计的基础。理解不同数据类型的特性和如何正确初始化这些类型非常...
C++ 学习笔记C++ 学习笔记C++ 学习笔记C++ 学习笔记002
【C++学习笔记】这份详尽的资源是为那些希望深入了解C++编程语言的人们精心准备的。C++是一种强大的、通用的编程语言,它的设计理念是面向对象,同时支持过程化编程,使得它在系统软件、应用软件、游戏开发、设备...
C++是一种强大的面向对象编程语言,它源自C语言并扩展了其功能,包括类、模板、异常处理等高级特性。以下是对C++基础知识点的详细解释: 1. **空头程序**:C++中的空头程序是一个没有实际操作的简单程序,通常用于...
《Effective Modern C++:改善C++11和C++14的42个具体做法(影印版)(英文版)》中包括以下主题:剖析花括号初始化、noexcept规范、完美转发、智能指针make函数的优缺点;讲解std∷move,std∷forward,rvalue引用和全局...
【达内C++课程笔记】是一份全面涵盖了C++编程语言以及相关技术的教程资源,由达内科技提供。这份笔记不仅包含C++的基础知识,还深入探讨了C++的高级特性和实际应用,旨在帮助学习者从入门到精通,掌握C++编程技能。 ...