`

C++11笔记二

 
阅读更多

一、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与变量

    该变量的值不允许改变;

 

 

 

 

  • 大小: 49.9 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics