今天的知识内容:


1)为什么会出现构造函数与析构函数

构造函数3种初始化

2)拷贝函数4种应用时机

【新发现】在拷贝构造函数里面 可以直接访问私有成员属性

两个已经初始化完毕的对象 用= 号赋值不会调用构造函数


3)匿名对象的去与留

GCC  与 VS环境编译的差异

一旦自定义了构造函数,你必须去调用


4)【强化】构造函数 与 析构函数的执行顺序强化练习

对象中包含有其他对象时,先初始化别人,再初始化自己

对象中包含有其他对象时,先析构自己,再析构别人



默认构造函数是浅拷贝

5)拷贝构造函数的深拷贝与浅拷贝

【注意】两个已经初始化完毕的对象 用= 号赋值不会调用构造函数,但是会调用拷贝构造函数


6)构造函数初始化列表

类中含有const属性变量,必须在构造函数的初始化列表赋值


7)匿名对象的生命周期,被接收 OR 无接收

8)在构造函数中调用构造函数,是一种危险的行为


9)C++ new  delete 基础数据类型,数组 ,对象

new delete 与 malloc free 互用


10)类中静态变量,所有对象公用

类中静态变量,静态函数基本调用

类中静态函数只能调用静态变量

11)面向对象模型初探--其实还是C语言的面向过程

12)this指针初步

今天作业








为什么会出现构造函数 与 析构函数

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	void init()
	{
		a = 1;
		b = 2;
	}
private:
	int a;
	int b;
};

int main()
{
	Test arr[3] ;
	arr[0].init();	//显式的执行初始化
	arr[1].init();
	arr[2].init();
	
	Test haha[19999] ;	//请问这个怎么去初始化

	return 0;
}

chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out




类的构造函数 与 析构函数

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
using namespace std;
class Test
{
public:
	Test()
	{
		cout << "构造函数 \n";
	}
	~Test()
	{
		cout << "析构函数 \n";
	}
};

int main()
{
	Test t1;
	Test t2;
	cout << "Hello World \n";
	return 0;
}

chunli@Linux:~/c++$ g++ -g -Wall main.cpp   && ./a.out 
构造函数 
构造函数 
Hello World 
析构函数 
析构函数




构造函数初始化变量:

析构函数释放内存

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class Test
{
public:
	Test()
	{
		a = 10;
		p = (char *)malloc(100);
		strcpy(p,"Hello Linux!");
		cout << "构造函数 \n";
	}
	void printf_var()
	{
		cout << a << endl;
		cout << p << endl;
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);
		}
		cout << "析构函数 \n";
	}
private:
	char *p;
	int a;
};
void fun()
{
	Test t1;
	Test t2;
	t1.printf_var();
}

int main()
{
	fun();
	cout << "-------------\n";
	return 0;
}

chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
构造函数 
构造函数 
10
Hello Linux!
析构函数 
析构函数 
-------------
chunli@Linux:~/c++$




类的构造函数3种初始化方式:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test()
	{
		cout << "没有默认参数的构造函数 \n";
	}

	Test(int _a)
	{
		a = _a;
		cout << "a=" << a <<"带有1个默认参数的构造函数 \n";
	}

	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << "  b=" << b<< "带有2个默认参数的构造函数 \n";
	}

	Test(const Test &OBJ)	//用一个对象初始化另一个对象
	{
		cout << "赋值构造函数构造函数 \n";
	}
	~Test()
	{
		cout << "析构函数 \n";
	}
private:
	int a;
	int b;
};
void fun()
{
	Test t1;	//调用无参数的构造函数

	//===== 	对象的初始化	==========
	/*1*/Test t2(1,2);
	//逗号表达式的最后一个值是整个表达式的值  g++ -Wall 编译有警告:逗号操作符的左操作数没有效果
	/*2*/Test t3 = (3,4,5,6,7);	//这个等号C++编译器做了增强
	/*3*/Test t4 = Test(1,4);/*匿名对象*/
	t1 = t4;	//对象的赋值,这个等号是普通的赋值操作

	Test t5(t2);
}

int main()
{
	fun();
	cout << "-------------\n";
	return 0;
}

正常编译:
chunli@Linux:~/c++$ g++  -g main.cpp  && ./a.out 
没有默认参数的构造函数 
a=1  b=2带有2个默认参数的构造函数 
a=7带有1个默认参数的构造函数 
a=1  b=4带有2个默认参数的构造函数 
赋值构造函数构造函数 
析构函数 
析构函数 
析构函数 
析构函数 
析构函数 
-------------


g++ -Wall编译
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
main.cpp: In function ‘void fun()’:
main.cpp:44:20: warning: left operand of comma operator has no effect [-Wunused-value]
  /*2*/Test t3 = (3,4,5,6,7);
                    ^
没有默认参数的构造函数 
a=1  b=2带有2个默认参数的构造函数 
a=7带有1个默认参数的构造函数 
a=1  b=4带有2个默认参数的构造函数 
赋值构造函数构造函数 
析构函数 
析构函数 
析构函数 
析构函数 
析构函数 
-------------
chunli@Linux:~/c++$



【拷贝构造函数应用的4种时机】

1,抛题 -- 乱码的出现:

#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(Test &OBJ)
	{
		cout << "赋值构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2 = t1;	
	//用t1 的参数初始化t2.会调用赋值构造函数,
	//如果没有编译器自己造,如果自定义了赋值构造函数,就直接调用
	t2.fun1();	//此时 输出属性值是乱码。因为我们自定义的构造函数什么也没有做
	return 0;
}

chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
拷贝构造函数被调用 
a=-336889536  b=32764





拷贝构造函数的使用:

第1种调用方法:Test t2 = t1;

chunli@Linux:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "拷贝构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2 = t1;	
	t2.fun1();	
	return 0;
}

chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
拷贝构造函数被调用 
a=101  b=151


拷贝构造函数的第2 中调用方式:Test t2(t1);

chunli@Linux:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "拷贝构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2(t1);	t2.fun1();
	return 0;
}

chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
拷贝构造函数被调用 
a=101  b=151


【答疑】对象的赋值操作t2 = t1 ,不会调用赋值构造函数:

chunli@Linux:~/c++$ cat main.cpp 
chunli@Linux:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "赋值构造函数被调用 \n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,5);	t1.fun1();
	Test t2(99,99);	t2.fun1();
	t2 = t1;	t2.fun1();
	return 0;
}

chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
2个参数
a=99  b=99  
a=1  b=5



赋值构造函数的第3中应用:函数

chunli@Linux:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;

class Test
{
public:
	Test(int _a,int _b)
	{
		cout << "2个参数\n";
		a = _a;
		b = _b;
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 100;
		b = OBJ.a + 150;
		cout << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a <<" b =" << b<< "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "a="<< a << "  ";
		cout << "b="<< b << "  \n";
	}
private:
	int a;
	int b;
};
void fun2(Test OBJ)	//会自动调用类的赋值构造函数
{
	OBJ.fun1();
}
void fun1()
{
	Test t1(1,5);	t1.fun1();
	Test t2 = t1;	t2.fun1();
	cout << "t2已经完成初始化\n";
	fun2(t2);	//t2实参会初始化形参OBJ,会调用赋值构造函数
}

int main()
{
	fun1();
	return 0;
}

chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
2个参数
a=1  b=5  
赋值构造函数被调用 
a=101  b=151  
t2已经完成初始化
赋值构造函数被调用 
a=201  b=251   
a=201 b =251析构函数被调用
a=101 b =151析构函数被调用
a=1 b =5析构函数被调用




【难点】赋值构造函数应用4 

【匿名对象的去和留】-- 去

1,没有对象接收函数的返回 

在VS编译下,返回的对象会调用一次析构函数,然后没人接收就析构掉

在GCC下,不会出现这种情况


【这第4种方法 VS环境 与 GCC 表现不一样】


//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;

class Test
{
public:
	Test(int _a, int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a << " b =" << b << "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
	Test A(66, 77);
	return A;
	//新的对象作为匿名对象返回
}

//用这个函数观测对象的生命周期
void fun2()
{
	//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
	//GCC 既不执行copy构造函数,也不执行析构函数
	fun1();
}

int main()
{
	fun2();
	getchar();
	return 0;
}
GCC编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
a=66 b =77构造函数初始化, 有2个参数
a=66 b =77析构函数被调用


VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b =78析构函数被调用



【难点】赋值构造函数应用4 

【匿名对象的去和留】-- 留

1,创建一个对象来接收函数的返回 

在VS编译下,返回的对象会调用一次析构函数,然后被接收就不会直接析构掉

在GCC下,不会出现这种情况

//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;

class Test
{
public:
	Test(int _a, int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a << " b =" << b << "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
	Test A(66, 77);
	return A;
	//新的对象作为匿名对象返回
}

//用这个函数观测对象的生命周期
void fun2()
{
	//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
	//GCC 既不执行copy构造函数,也不执行析构函数
	//注意此时是一个新的对象,VS会调用copy构造函数,不会析构
	Test b = fun1();
	b.fun1();
}

int main()
{
	fun2();
	getchar();
	return 0;
}
GCC下编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
a=66 b =77构造函数初始化, 有2个参数
a=66  b=77  
a=66 b =77析构函数被调用

VS下编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67  b=78
a=67 b =78析构函数被调用




【难点】赋值构造函数应用4 

【匿名对象的去和留】-- 去

1,创建一个对象来接收函数的返回 

在VS编译下,返回的对象会调用一次析构函数,然后被接收就不会直接析构掉

在GCC下,不会出现这种情况

chunli@Linux:~/c++$ cat main.cpp 
//拷贝构造函数应用的4种时机
#include <iostream>
#include <stdio.h>
using namespace std;

class Test
{
public:
	Test(int _a, int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a << " b =" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		cout << "a=" << a << " b =" << b << "赋值构造函数被调用 \n";
	}
	~Test()
	{
		cout << "a=" << a << " b =" << b << "析构函数被调用\n";
	}
	void fun1()
	{
		cout << "执行对象的成员函数  ";
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

//函数返回一个Test 对象 (复杂类型的)
//返回的是一个新的匿名对象
//所以会调用匿名对象类的copy构造函数
Test fun1()
{
	cout << "fun1 start \n" ;
	//A  是局部变量,会被析构
	Test A(66, 77);
	return A;
	//新的对象作为匿名对象返回
	cout << "fun1 end \n" ;
}

//用这个函数观测对象的生命周期
void fun2()
{
	//微软VS环境有效:用b对象b来接返回的匿名对象,不会直接析构
	//GCC 既不执行copy构造函数,也不执行析构函数
	cout << "fun2 start \n" ;
	Test b(1,2);
	b.fun1();
	cout << "因为此时匿名对象并没有转换成新对象,匿名对象就会析构\n";
	b =  fun1();
	b.fun1();
	cout << "fun2 end \n" ;
}

int main()
{
	fun2();
	getchar();
	return 0;
}
chunli@Linux:~/c++$ 
GCC编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 

fun2 start 
a=1 b =2构造函数初始化, 有2个参数
执行对象的成员函数  a=1  b=2  
因为此时匿名对象并没有转换成新对象,匿名对象就会析构
fun1 start 
a=66 b =77构造函数初始化, 有2个参数
a=66 b =77析构函数被调用
执行对象的成员函数  a=66  b=77  
fun2 end 
a=66 b =77析构函数被调用
chunli@Linux:~/c++$ 

VS编译运行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
fun2 start
a=1 b =2构造函数初始化, 有2个参数
执行对象的成员函数  a=1  b=2
因为此时匿名对象并没有转换成新对象,匿名对象就会析构
fun1 start
a=66 b =77构造函数初始化, 有2个参数
a=67 b =78赋值构造函数被调用
a=66 b =77析构函数被调用
a=67 b =78析构函数被调用
执行对象的成员函数  a=67  b=78
fun2 end
a=67 b =78析构函数被调用


结论: 有关 匿名对象的去和留【VS环境】

如果用匿名对象  初始化 另外一个同类型的对象, 匿名对象 转成有名对象

如果用匿名对象  赋值给 另外一个同类型的对象, 匿名对象 被析构


老师的笔记与图:wKioL1dz59nAia2JAAJn6FZ1dno325.jpg老师的程序:

#include <iostream>
using namespace std;


class Location 
{ 
public:
	Location( int xx = 0 , int yy = 0 ) 
	{ 
		X = xx ;  Y = yy ;  cout << "Constructor Object.\n" ; 
	}

	//copy构造函数  完成对象的初始化
	Location(const Location & obj) //copy构造函数 
	{
		X = obj.X; Y = obj.Y;
	}
	~Location() 
	{ 
		cout << X << "," << Y << " Object destroyed." << endl ; 
	}
	int  GetX () { return X ; }		int GetY () { return Y ; }
private :   int  X , Y ;
} ;

//g函数 返回一个元素 
//结论1 : 函数的返回值是一个元素 (复杂类型的), 返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)

//
//结论2: 有关 匿名对象的去和留
//如果用匿名对象  初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象  赋值给 另外一个同类型的对象, 匿名对象 被析构

//
//你这么写代码,设计编译器的大牛们:
//我就给你返回一个新对象(没有名字 匿名对象)
Location g()
{
	Location A(1, 2);
	return A;
}

//
void objplay2()
{
	g(); 
}

//
void objplay3()
{
	//用匿名对象初始化m 此时c++编译器 直接把匿名对转成m;(扶正) 从匿名转成有名字了m
	Location m = g(); 
	printf("匿名对象,被扶正,不会析构掉\n");
	cout<<m.GetX()<<endl;;
}

void objplay4()
{
	//用匿名对象 赋值给 m2后, 匿名对象被析构
	Location m2(1, 2);
	m2 = g();
	printf("因为用匿名对象=给m2, 匿名对象,被析构\n");
	cout<<m2.GetX()<<endl;;
}
void main()
{
	//objplay2();
	//objplay3();
	objplay4();
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}




【回顾上午】




构造函数初始化的三种方式:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
class Test
{
public:
	Test(int _a)
	{
		a = _a;
		cout << "a=" << a << "构造函数初始化, 有1个参数\n";
	}
	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a  << "  b= " << b << "构造函数初始化, 有2个参数\n";
	}
	void fun()
	{
		cout << "执行对象的成员函数  ";
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

int main()
{
	Test t1(1,2);		t1.fun();//C++编译器自动调用构造函数
	Test t2 = (1,5,6);	t2.fun();//C++编译器自动调用构造函数
	Test t3 = Test(4,5);		//程序员手动调用构造函数
	return 0;
}
chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
a=1  b= 2构造函数初始化, 有2个参数
执行对象的成员函数  a=1  b=2  
a=6构造函数初始化, 有1个参数
执行对象的成员函数  a=6  b=0  
a=4  b= 5构造函数初始化, 有2个参数




【回顾】拷贝构造函数的4种使用时机

GCC编译器 与 VS编译有些区别:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(int _a)
	{
		a = _a;
		cout << "a=" << a << "构造函数初始化, 有1个参数\n";
	}
	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a  << " b=" << b << "构造函数初始化, 有2个参数\n";
	}
	Test(const Test &OBJ)
	{
		a = OBJ.a + 1;
		b = OBJ.b + 1;
		num++;
		cout << "第"<< num <<"次调用拷贝构造函数";
		cout << "a=" << a << "  b=" << b <<endl ;
	}
	~Test()
	{
		cout << "a=" << a << " b=" << b << "析构函数被调用\n";
	}
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << "  ";
		cout << "b=" << b << "  \n";
	}
private:
	int a;
	int b;
};

void fun1()
{
	cout << "拷贝函数的调用时机,第1种和第2种 \n";
	Test t1(1,2);		t1.fun();
	Test t2 = t1;		t2.fun();	//C++编译器会调用拷贝构造函数
	Test t3(t2) ;		t3.fun();	//C++编译器会调用拷贝构造函数
}
void fun2(Test OBJ)
{
	cout << "拷贝函数的调用时机,第3种\n";
}

Test fun3()	//返回一个Test类
{
	cout << "拷贝函数的调用时机,第4种\n";
	Test t1(7,8);
	return t1;
}

int main()
{
	fun1();	//拷贝函数的调用时机,第1种和第2种
	Test t1(4,5);	fun2(t1);	//拷贝函数的调用时机,第3种
	Test t2 = fun3();		//弄了一个新的对象接受了匿名对象,这个不会直接执行析构函数
	Test t3(11,12);	t3 =  fun3();	//t3不是一个新的对象,匿名类会执行析构函数
	return 0;
}
1 GCC编译执行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
拷贝函数的调用时机,第1种和第2种 
a=1 b=2构造函数初始化, 有2个参数
执行对象的成员函数   a=1  b=2  
第1次调用拷贝构造函数a=2  b=3
执行对象的成员函数   a=2  b=3  
第2次调用拷贝构造函数a=3  b=4
执行对象的成员函数   a=3  b=4  
a=3 b=4析构函数被调用
a=2 b=3析构函数被调用
a=1 b=2析构函数被调用
a=4 b=5构造函数初始化, 有2个参数
第3次调用拷贝构造函数a=5  b=6
拷贝函数的调用时机,第3种
a=5 b=6析构函数被调用
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
a=11 b=12构造函数初始化, 有2个参数
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
a=7 b=8析构函数被调用
a=7 b=8析构函数被调用
a=7 b=8析构函数被调用
a=4 b=5析构函数被调用
chunli@Linux:~/c++$ 

2 VS环境编译执行:
C:\Users\chunli\Documents\c_c++\ConsoleApplication2\Debug>ConsoleApplication2
拷贝函数的调用时机,第1种和第2种
a=1 b=2构造函数初始化, 有2个参数
执行对象的成员函数   a=1  b=2
第1次调用拷贝构造函数a=2  b=3
执行对象的成员函数   a=2  b=3
第2次调用拷贝构造函数a=3  b=4
执行对象的成员函数   a=3  b=4
a=3 b=4析构函数被调用
a=2 b=3析构函数被调用
a=1 b=2析构函数被调用
a=4 b=5构造函数初始化, 有2个参数
第3次调用拷贝构造函数a=5  b=6
拷贝函数的调用时机,第3种
a=5 b=6析构函数被调用
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
第4次调用拷贝构造函数a=8  b=9
a=7 b=8析构函数被调用
a=11 b=12构造函数初始化, 有2个参数
拷贝函数的调用时机,第4种
a=7 b=8构造函数初始化, 有2个参数
第5次调用拷贝构造函数a=8  b=9
a=7 b=8析构函数被调用
a=8 b=9析构函数被调用
a=8 b=9析构函数被调用
a=8 b=9析构函数被调用
a=4 b=5析构函数被调用


在拷贝构造函数里面 可以直接访问私有成员属性


chunli@Linux:~$ cat main1.cpp 
#include <iostream>
#include <string.h>
using namespace std;

class Str
{
public:
	Str(const char *p = NULL)
	{
		if(p == NULL)
		{
			this->p = NULL;	
			this->len = 0;
		}
		else
		{
			this->len = strlen(p);
			this->p = new char[this->len +1 ];
			strncpy(this->p,p,this->len);
			this->p[this->len] = '\0';
		}
	}
	Str(const Str &from)
	{
		this->len = from.len;
		this->p = new char[this->len +1];
		strncpy(this->p,from.p,this->len);
		this->p[this->len] = '\0';
	}
	void print()
	{
		if(this->p != NULL)
		{
			cout<<this->p<<endl;
		}
	}
private:
	int len;
	char *p;
};

int main()
{
	Str s1;				s1.print();
	Str s2("Hello World!");		s2.print();
	Str s3 = "Hello Linux!";	s3.print();
	Str s4 = s3;			s4.print();
	return 0;
}
chunli@Linux:~$ g++  -o run main1.cpp  && ./run 
Hello World!
Hello Linux!
Hello Linux!
chunli@Linux:~$




【关于默认构造函数】

默认构造函数:

二个特殊的构造函数

1)默认无参构造函数

当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2)默认拷贝构造函数

当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制






【构造函数调用规则研究 】

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数

2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数

3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数

4 )默认拷贝构造函数成员变量简单赋值

总结:只要你写了构造函数,那么你必须用。





1,【当你定义了构造函数,你必须使用】:

先演示没有任何构造函数的情景:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << " b=" << b << "  \n";
	}
private:
	int a;
	int b;
};
int main()
{
	Test t1;	t1.fun();
	return 0;
}

编译运行OK:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
执行对象的成员函数   a=2071403600 b=32765




当类中定义了构造函数,如果不使用,对象初始化就会报错!


示范:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(int _a,int _b)
	{
		a = _a;
		b = _b;
		cout << "a=" << a  << " b=" << b << "构造函数初始化, 有2个参数\n";
	}
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << " b=" << b << "  \n";
	}
private:
	int a;
	int b;
};
int main()
{
	Test t1;	t1.fun();//定义一个对象
	return 0;
}

编译运行:直接报错
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
main.cpp: In function ‘int main()’:
main.cpp:43:7: error: no matching function for call to ‘Test::Test()’
  Test t1; t1.fun();
       ^



手动定义一个空的构造函数,编译通过!

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test()
	{

	}
	void fun()
	{
		cout << "执行对象的成员函数   ";
		cout << "a=" << a << " b=" << b << "  \n";
	}
private:
	int a;
	int b;
};
int main()
{
	Test t1;	t1.fun();
	return 0;
}


chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
执行对象的成员函数   a=89452272 b=32765  
chunli@Linux:~/c++$




【结论】

在类的定义时,只要写了构造函数,编译器就不会再自动为你定义无参的构造函数,这个构造函数必须要使用!


构造析构阶段性总结 

1)构造函数是C++中用于初始化对象状态的特殊函数

2)构造函数在对象创建时自动被调用

3)构造函数和普通成员函数都遵循重载规则

4)拷贝构造函数是对象正确初始化的重要保证

5)必要的时候,必须手工编写拷贝构造函数 




【浅拷贝 与 深拷贝】


wKioL1dz6dLx6SXlAAHpvErchTY730.png




浅拷贝  示范,程序编译通过,运行就死掉,

因为释放已经释放过的内存,所以死掉

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(const char *str)
	{
		cout << "I'm init....\n";
		len  =  strlen(str);
		p = (char *)malloc(len +1);
		strcpy(p,str);
		*(p + len) = '\0';
		
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);		
			p = NULL;
			len = 0;
		}
		cout << "I'm Free!\n";
	}
	void show()
	{
		cout << p <<"\n";
	}
private:
	char *p;
	int len;
};

void fun1()
{
	Test t1("hello world");	t1.show();
	Test t2(t1);		t2.show();
	
}
int main()
{
	fun1();
	return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
I'm init....
hello world
hello world
I'm Free!
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001a93010 ***
Aborted (core dumped)
chunli@Linux:~/c++$ 

gdb调试:
chunli@Linux:~/c++$ gdb ./a.out 
(gdb) run
(gdb) where
#5  0x0000000000400b35 in Test::~Test (this=0x7fffffffead0, __in_chrg=<optimized out>) at main.cpp:23

会看到 at main.cpp:23
这一行就是 free(p);




手动写一个拷贝构造函数吗,完成深拷贝:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(const char *str)
	{
		cout << "I'm init....\n";
		len  =  strlen(str);
		p = (char *)malloc(len +1);
		strcpy(p,str);
		*(p + len) = '\0';
		
	}
	Test(const Test &obj)	// 当外部调用 Test t2(t1);
	{
		//深度拷贝
		len = obj.len;
		p = (char *)malloc(len +1);;
		strcpy(p,obj.p);
		*(p + len) = '\0';
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);		
			p = NULL;
			len = 0;
		}
		cout << "I'm Free!\n";
	}
	void show()
	{
		cout << p <<"\n";
	}
private:
	char *p;
	int len;
};

void fun1()
{
	Test t1("hello world");	t1.show();
	Test t2(t1);		t2.show();
        //Test t3 = t1;         =等号也是浅拷贝
	
}
int main()
{
	fun1();
	return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
I'm init....
hello world
hello world
I'm Free!
I'm Free!



还是会存在宕掉的可能:

Test t1("hello world!");t1.show();

Test t2("hello Linux!");t2.show();

t1 = t2;//=等号也是浅拷贝

这样会再次导致一片内存两次free,继续宕机!

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;
class Test
{
public:
	Test(const char *str)
	{
		cout << "I'm init....\n";
		len  =  strlen(str);
		p = (char *)malloc(len +1);
		strcpy(p,str);
		*(p + len) = '\0';
		
	}
	Test(const Test &obj)	// 当外部调用 Test t2(t1);
	{
		//深度拷贝
		len = obj.len;
		p = (char *)malloc(len +1);;
		strcpy(p,obj.p);
		*(p + len) = '\0';
		cout << "I'm in copy \n";
	}
	~Test()
	{
		if(p != NULL)
		{
			free(p);		
			p = NULL;
			len = 0;
		}
		cout << "I'm Free!\n";
	}
	void show()
	{
		cout << p <<"\n";
	}
private:
	char *p;
	int len;
};

void fun1()
{
	Test t1("hello world!");	t1.show();
	Test t2("hello Linux!");	t2.show();
	t1 = t2;			//=等号也是浅拷贝
	
}
int main()
{
	fun1();
	return 0;
}

编译通过,运行出错!
chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
I'm init....
hello world!
I'm init....
hello Linux!
I'm Free!
*** Error in `./a.out': double free or corruption (fasttop): 0x000000000249b030 ***
Aborted (core dumped)

【gdb调试】:
chunli@Linux:~/c++$ gdb ./a.out 
(gdb) run
(gdb) where
#5  0x0000000000400b43 in Test::~Test (this=0x7fffffffead0, __in_chrg=<optimized out>) at main.cpp:32
32行就是    free(p);




构造函数 初始化 列表:

1,首先会执行被组合对象的构造函数

2,如果组成对象有多个,安照对象定义的顺序初始化,而不是按照列表的顺序

3,被组合对象的析构顺序与构造顺序相反

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;

class Test1
{
public:
	Test1(int _a)
	{
		a = _a;
		cout << a << " Test1 I'm init....\n";
	}
	~Test1()
	{
		cout << a << " Test1 I'm Free!\n";
	}
private:
	int a;
};

class Test2
{
public:
	//在初始化参数的同时,将Test1对象完成初始化
	Test2(int _a):t11(1),t12(2)
	{
		a = _a;
		cout << "Test2  1 I'm init....\n";
	}
	//经典用法,参数传递
	Test2(int _a,int _b,int _c,int _d):t11(_c),t12(_d)
	{
		a = _a;
		b = _b;
		cout << "Test2 2 I'm init....\n";
	}
	~Test2()
	{
		cout << "Test2 I'm Free!\n";
	}
private:
	int a;
	int b;
	Test1 t11;
	Test1 t12;
};

int main()
{
//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
	Test2 t22(1,3,1,4);
	return 0;
}



chunli@Linux:~/c++$ g++ -Wall -g main.cpp  && ./a.out 
1 Test1 I'm init....
4 Test1 I'm init....
Test2 2 I'm init....
Test2 I'm Free!
4 Test1 I'm Free!
1 Test1 I'm Free!
chunli@Linux:~/c++$



如果类的属性有const这么一个属性,必须在构造函数的初始化列表中

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
int num = 0;

class Test1
{
public:
	Test1(int _a)
	{
		a = _a;
		cout << a << " Test1 I'm init....\n";
	}
	~Test1()
	{
		cout << a << " Test1 I'm Free!\n";
	}
private:
	int a;
};

class Test2
{
public:
	//在初始化参数的同时,将Test1对象完成初始化
	Test2(int _a):t11(1),t12(2),c(3)
	{
		a = _a;
		cout << "Test2  1 I'm init....\n";
	}
	//经典用法,参数传递
	Test2(int _a,int _b,int _c,int _d):t11(_c),t12(_d),c(_d)
	{
		a = _a;
		b = _b;
		cout << "Test2 2 I'm init....\n";
	}
	~Test2()
	{
		cout << "Test2 I'm Free!\n";
	}
private:
	int a;
	int b;
	const int c;
	Test1 t11;
	Test1 t12;
};

int main()
{
//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
	Test2 t22(1,3,1,4);
	return 0;
}


编译运行:
chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
1 Test1 I'm init....
4 Test1 I'm init....
Test2 2 I'm init....
Test2 I'm Free!
4 Test1 I'm Free!
1 Test1 I'm Free!




构造函数,拷贝函数,析构函数综合练习题,根据程序,写出屏幕输出:

【提醒:】

对象中包含有其他对象时,先初始化别人,再初始化自己

对象中包含有其他对象时,先析构自己,再析构别人

chunli@Linux:~/c++$ cat 123.cpp 
//对象做函数参数
//1 研究拷贝构造 
//2 研究构造函数,析构函数的调用顺序
//总结 构造和析构的调用顺序

#include "iostream"
#include "stdio.h"
using namespace std;

class ABCD 
{
public:
	ABCD(int a, int b, int c)
	{
		this->a = a;
		this->b = b;
		this->c = c;
		printf("ABCD() construct, a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
	}
	~ABCD()
	{
		printf("~ABCD() construct,a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
	}
	int getA() 
	{
		return this->a;
	}
protected:
private:
	int a;
	int b;
	int c;
};


class MyE
{
public:
	MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
	{
		cout<<"MyD()"<<endl;
	}
	~MyE()
	{
		cout<<"~MyD()"<<endl;
	}
	MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
	{
		printf("MyD(const MyD & obj)\n");
	}

protected:
	//private:
public:
	ABCD abcd1; //c++编译器不知道如何构造abc1
	ABCD abcd2;
	const int m;

};

int doThing(MyE mye1)
{
	printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA()); 
	return 0;
}

int run2()
{
	MyE myE;
	doThing(myE);
	return 0;
}

//
int run3()
{
	printf("run3 start..\n");

	//ABCD(400, 500, 600); //临时对象的生命周期  

	ABCD abcd = ABCD(100, 200, 300);
	//若直接调用构造函数呢?
	//想调用构造函数对abc对象进行再复制,可以吗?
	//在构造函数里面调用另外一个构造函数,会有什么结果?

	printf("run3 end\n");
	return 0;
}

int main()
{
	run2();
	//run3();
	return 0;
}


先不要看答案,自己分析一下【注意子对象的构造  与 析构 顺序!】

chunli@Linux:~/c++$ g++ 123.cpp  && ./a.out 
ABCD() construct, a:1,b:2,c:3  
ABCD() construct, a:4,b:5,c:6  
MyD()
ABCD() construct, a:7,b:8,c:9  
ABCD() construct, a:10,b:11,c:12  
MyD(const MyD & obj)
doThing() mye1.abc1.a:7 
~MyD()
~ABCD() construct,a:10,b:11,c:12  
~ABCD() construct,a:7,b:8,c:9  
~MyD()
~ABCD() construct,a:4,b:5,c:6  
~ABCD() construct,a:1,b:2,c:3  
chunli@Linux:~/c++$


匿名对象的生命周期 强化训练:

分析下列程序的输出结果:

chunli@Linux:~/c++$ cat 123.cpp 
#include "iostream"
#include "stdio.h"
using namespace std;
class ABCD 
{
public:
	ABCD(int a, int b, int c)
	{
		this->a = a;
		this->b = b;
		this->c = c;
		printf("ABCD() construct, a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
	}
	~ABCD()
	{
		printf("~ABCD() construct,a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
	}
	int getA() 
	{
		return this->a;
	}
protected:
private:
	int a;
	int b;
	int c;
};

class MyE
{
public:
	MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
	{
		cout<<"MyD()"<<endl;
	}
	~MyE()
	{
		cout<<"~MyD()"<<endl;
	}
	MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
	{
		printf("MyD(const MyD & obj)\n");
	}

protected:
public:
	ABCD abcd1; //c++编译器不知道如何构造abc1
	ABCD abcd2;
	const int m;

};

int run2()
{
	printf("run2 start..\n");
	ABCD(400, 500, 600); //临时对象的生命周期  
	printf("run2 end\n");
	return 0;
}
int run3()
{
	printf("run3 start..\n");
	ABCD abcd = ABCD(100, 200, 300);	//这个动作不会调用拷贝构造函数
	printf("run3 end\n");
	return 0;
}

int main()
{
	run2();
	cout << "\n\n";
	run3();
	return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ 123.cpp  && ./a.out 
run2 start..
ABCD() construct, a:400,b:500,c:600  
~ABCD() construct,a:400,b:500,c:600  
run2 end


run3 start..
ABCD() construct, a:100,b:200,c:300  
run3 end
~ABCD() construct,a:100,b:200,c:300



在构造函数中调用构造函数 是一种危险的行为:

chunli@Linux:~/c++$ cat 123.cpp 
#include "iostream"
#include "stdio.h"
using namespace std;


//构造中调用构造是危险的行为
class MyTest
{
public:
	MyTest(int a, int b, int c)
	{
		this->a = a;
		this->b = b;
		this->c = c;
	}

	MyTest(int a, int b)
	{
		this->a = a;
		this->b = b;
		MyTest(a, b, 100); //产生新的匿名对象
	}
	~MyTest()
	{
		printf("MyTest~:%d, %d, %d\n", a, b, c);
	}

protected:
private:
	int a;
	int b;
	int c;

public:
	int getC() const { return c; }
	void setC(int val) { c = val; }
};

int main()
{
	MyTest t1(1, 2);
	printf("c:%d", t1.getC()); //请问c的值是?
	return 0;
}
看分析图:
编译运行:
chunli@Linux:~/c++$ g++ 123.cpp  && ./a.out 
MyTest~:1, 2, 100
c:4196128MyTest~:1, 2, 4196128



C++ new delete 

1,基础类型:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

int main()
{

	int *p1 = (int *)malloc(sizeof(int));
	*p1 = 10;	
	cout << *p1 << endl;
	free(p1);

	int *p2 = new int;
	*p2 = 20;
	cout << *p2 << endl;
	detele p2;

	//分配的同时初始化
	int *p3 = new int(30);
	cout << *p3 << endl;
	delete p3;

	return 0;
}



chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
10
20
30




数组类型的建立与释放:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

int main()
{
	int *p1 = new int[10];
	p1[1] = 1;
	delete [] p1;	//数组的释放
	return 0;
}



chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
chunli@Linux:~/c++$


new delete 动态创建对象

C 与 C++ 的差异

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

class Test
{
public:
	Test(int _a)
	{
		a = _a;
		cout << a << " init \n";
	}
	~Test()
	{
		cout <<a<< " free \n";
	}
private:
	int  a;
};

int main()
{
//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数   delete操作符 能执行类的析构函数
	//C
	Test *p1  =(Test *)malloc(sizeof(Test));
	free(p1);

	//C++
	Test *p2 = new Test(10);
	delete p2;
	
	return 0;
}



chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
10 init 
10 free



new delete深入

【结论】

malloc只会分配内存的大小,不会调用类的构造函数,free也不会调用类的析构函数

但是new会调用类的构造函数,完成类的初始化,delete会调用类的析构函数   

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

class Test
{
public:
	Test(int _a)
	{
		a = _a;
		cout << a << " init \n";
	}
	~Test()
	{
		cout <<a<< " free \n";
	}
private:
	int  a;
};

int main()
{
	//malloc只会分配内存的大小,不会调用类的构造函数,free也不会调用类的析构函数
	//但是new会调用类的构造函数,完成类的初始化,delete会调用类的析构函数	

	// malloc -- > delete
	int *p1 = (int *)malloc(sizeof(int));
	*p1 = 10;	
	cout << *p1 << endl;
	delete p1;

	// new -- > free
	int *p2 = new int(30);
	cout << *p2 << endl;
	free(p2);

	// new -- > free
	int *p3 = new int[10];
	p3[1] = 1;
	free(p3);
	
	// malloc -- > delete	触发析构
	Test *p4  =(Test *)malloc(sizeof(Test));
	delete p4;

	// new -- > free	触发构造
	Test *p5 = new Test(10);
	free(p5);
	
	return 0;
}



chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
10
30
0 free 
10 init




类中静态成员,所有对象都共用:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

class Test
{
public:
	void print_c()
	{
		cout << c << endl;
	}
	void add_c()
	{
		c += 1;
	}
	Test(int _a)
	{
		a = _a;
		//cout << a << " init \n";
	}
	~Test()
	{
		//cout <<a<< " free \n";
	}
private:
	int  a;
	int  b;
	static int c;
};
int Test::c = 10;

int main()
{
	Test t1(1);	t1.add_c();	t1.print_c();
	Test t2(2);	t2.add_c();	t2.print_c();
	Test t3(3);	t3.add_c();	t3.print_c();
	return 0;
}



chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
11
12
13



类中静态成员函数的基本调用:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

class Test
{
public:
	static void print_c()
	{
		cout << c << endl;
	}
	void add_c()
	{
		c += 1;
	}
	Test(int _a)
	{
		a = _a;
		//cout << a << " init \n";
	}
	~Test()
	{
		//cout <<a<< " free \n";
	}
private:
	int  a;
	int  b;
	static int c;
};
int Test::c = 10;

int main()
{
	Test t1(1);	t1.add_c();	
	//静态成员函数的调用方法
	t1.print_c();
	Test::print_c();
	return 0;
}



chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
11
11


在类中静态函数中不能使用普通成员变量,普通成员变量

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

class Test
{
public:
	static void print_c()
	{
		cout << c << endl;
		cout << a << endl;
	}
	void add_c()
	{
		c += 1;
	}
	Test(int _a)
	{
		a = _a;
	}
	~Test()
	{
	}
private:
	int  a;
	static int c;
};
int Test::c = 10;

int main()
{
	Test t1(1);	t1.add_c();	
	//静态成员函数的调用方法
	t1.print_c();
	Test::print_c();
	return 0;
}

编译就报错:
chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
main.cpp: In static member function ‘static void Test::print_c()’:
main.cpp:27:7: error: invalid use of member ‘Test::a’ in static member function
  int  a;
       ^


面向对象模型  初探:

前言

C++对象模型可以概括为以下2部分:

1. 语言中直接支持面向对象程序设计的部分,主要涉及如构造函数、析构函数、虚函数、继承(单继承、多继承、虚继承)、多态等等。

2. 对于各种支持的底层实现机制。

c语言中,“数据”和“处理数据的操作(函数)”是分开来声明的,也就是说,语言本身并没有支持“数据和函数”之间的关联性。在c++中,通过抽象数据类型(abstractdata typeADT),在类中定义数据和函数,来实现数据和函数直接的绑定

概括来说,在C++类中有两种成员数据:staticnonstatic;三种成员函数:staticnonstaticvirtual


C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类。从计算机的角度,程序依然由数据段和代码段构成。

C++编译器如何完成面向对象理论到计算机程序的转化?

换句话:C++编译器是如何管理类、对象、类和对象之间的关系

具体的说:具体对象调用类中的方法,那,c++编译器是如何区分,是那个具体的类,调用这个方法那?




wKioL1d0tZ2TKpl_AADoQ-4C1Ec406.png


根据程序,写出屏幕输出结果:

chunli@Linux:~/c++$ cat main.cpp 
#include "iostream"
#include "stdio.h"

using namespace std;

class C1
{
public:
	int i;  //4
	int j; //4
	int k;  //4
protected:
private:
}; //12

class C2
{
public:
	int i; 
	int j; 
	int k; 

	static int m; //4
public:
	int getK() const { return k; } //4
	void setK(int val) { k = val; }  //4

protected:
private:
}; 

struct S1
{
	int i;
	int j;
	int k;
}; 

struct S2
{
	int i;
	int j;
	int k;
	static int m;
};

int main()
{
	printf("c1:%ld \n", sizeof(C1));
	printf("c2:%ld \n", sizeof(C2));
	printf("s1:%ld \n", sizeof(S1));
	printf("s2:%ld \n", sizeof(S2));
}
编译运行:
chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
c1:12 
c2:12 
s1:12 
s2:12

通过上面的案例,我们可以的得出:

1C++类对象中的成员变量和成员函数是分开存储的

成员变量:

普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式

静态成员变量:存储于全局数据区中

成员函数:存储于代码段中。

问题出来了:很多对象共用一块代码?代码是如何区分具体对象的那?

 

换句话说:int getK() const { return k; },代码是如何区分,具体obj1obj2obj3对象的k




this指针初步:

chunli@Linux:~/c++$ cat main.cpp 
#include <iostream>
#include <stdlib.h>
using namespace std;

class Test
{
public:
	Test(int a,int b)
	{	
		this->a = a;
		this->b = b;
	}
	void print_var()
	{
		cout << a << " " << b << endl; 
	}
private:
	int  a;
	int  b;
};

int main()
{
	Test t1(1,3);	t1.print_var();	
	Test t2(5,7);	t2.print_var();	
	return 0;
}
编译运行:
chunli@Linux:~/c++$ g++ -g main.cpp  && ./a.out 
1 3
5 7
chunli@Linux:~/c++$

1C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!

2C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。

3、静态成员函数、成员变量属于类

静态成员函数与普通成员函数的区别

静态成员函数不包含指向具体对象的指针

普通成员函数包含一个指向具体对象的指针



今天作业: