C++--day06
目录:
1. C的提高 1-131P 时间七天
2. C++的基础 132-286P 时间八天
3. C++的提高 287-378P 时间五天
4. C/C++的数据结构 379-482P 时间五天
5. C/C++的设计模式基础 483-540P 时间三天
视频资料:https://www.bilibili.com/video/av27904891?from=search&seid=10891514449061956870
P179 copy构造函数调用时机4-函数返回值是匿名对象
#include<iostream> using namespace std;class Location { public:Location( int xx = 0 , int yy = 0 ) { X = xx ; Y = yy ; cout << "Constructor Object.\n"<<endl ; }Location( const Location & p ) //拷贝构造函数 完成对象的初始化 { X = p.X ; Y = p.Y ; cout << "Copy_constructor called." << endl ; }~Location() { cout << X << "," << Y << " Object destroyed." << endl ; }int GetX () { return X ;} int GetY () { return Y ;} private : int X , Y ; } ;//g函数返回一个元素
//结论:函数的返回值是一个元素(复杂类型),返回的是一个新的匿名对象 所以会调用匿名对象类的拷贝对象函数
输出结果
P180 copy构造函数调用时机3-函数返回值是匿名对象去和留的剖析
#include<iostream> using namespace std;class Location { public:Location( int xx = 0 , int yy = 0 ) { X = xx ; Y = yy ; cout << "Constructor Object.\n"<<endl ; }Location( const Location & p ) //拷贝构造函数 完成对象的初始化 { X = p.X ; Y = p.Y ; cout << "Copy_constructor called." << endl ; }~Location() { cout << X << "," << Y << " Object destroyed." << endl ; }int GetX () { return X ;} int GetY () { return Y ;} private : int X , Y ; } ; //g函数返回一个元素 //结论1:函数的返回值是一个元素(复杂类型),返回的是一个新的匿名对象(所以会调用匿名对象类的拷贝对象函数) //结论2:有关匿名对象的去和留 // 如果用匿名对象初始化另外一个同类型的对象,匿名匿名对象,转成有名对象 // 如果用匿名对象赋值给另外一个同类型的对象,匿名对象被析构//这么写代码,编译器返回一个新对象(没有名字的匿名对象) Location g() {Location A(1,2);return A; } // void objplay1() {g();} void objplay2() {//用匿名对象初始化m,此时C++编译器直接把匿名对象转成m;从匿名转成有名字了Location m=g();printf("匿名对象被扶正,不会被析构\n");cout<<m.GetX()<<endl;} void objplay3() {//用匿名对象赋值给m2,匿名对象会析构Location m2(1,2);m2=g();printf("因为用匿名对象=给m2,匿名对象被析构\n");cout<<m2.GetX()<<endl;} void main() {objplay3();system("pause"); }
P181 构造和析构的重点整理
P183 构造和析构的总结
1、 构造函数的基础
2、 构造函数的分类
有参构造函数
无参构造函数
赋值构造函数
默认构造函数
3、拷贝函数的调用时机
4、C++编译器提供的构造函数 PK 对象显示初始化方案
5、拷贝构造函数
6、构造函数调用规则研究
若定义构造函数,则必须调用
若不定义构造函数 使用C++编译提供的构造函数
7、深拷贝和浅拷贝
8、构造函数初始化列表
对象互组
9、构造和析构的调用顺序研究
先调用成员变量的构造函数,再调用自己的构造函数
#include <iostream> using namespace std;void objplaymain71();class Test { public:Test(){a = 0; b = 0;cout << "无参数构造函数 自动被调用" <<endl;}Test(int _a) //有参数构造函数 {a = _a;b = 0;}Test(const Test& obj) //copy构造函数 作用: 用一个对象初始化另外一个对象 {a = obj.a + 100;b = obj.b + 100;}void printT(){cout << "a:" << a << "b: "<<b<< endl; }~Test(){cout<<"我是析构函数 对象生命周期结束时,会被c++编译器自动调用" <<endl;}protected: private:int a;int b; };// 第3种调用时机 void printTest(Test t) {; }// 1 和 2 void objplaymain72() {// Test t1(1); //ok Test t2(t1); Test t3 = t1; //会调用copy构造函数 printTest(t3); }//copy构造函数的第4种调用时机 //返回一个元素 匿名对象 Test getTestObj() {Test t(1);return t; }void TestNoNameObj() {Test myt1 =getTestObj(); //用匿名对象初始化 另外一个对象 Test myt2(1);myt2 = getTestObj(); //用匿名对象 给 另外一个对象 赋值 匿名对象被析构 }int main() {//objplaymain(); objplaymain72();TestNoNameObj();cout<<"hello..."<<endl;system("pause");return 0; }void objplaymain71() {Test t1; //ok//Test t2() ; //调用无参数构造函数的 错误方法//t2.printT();// Test t3(1); //c++编译器自动的调用构造函数Test t4 = 4; //c++编译器自动的调用构造函数Test t5 = Test(5); //程序员手工的调用构造函数// Test t6 = t1;return ; }
P184 构造函数的调用规则研究
默认构造函数
二个特殊的构造函数
1)默认无参构造函数
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
2)默认拷贝构造函数
当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制
构造函数调用规则研究
1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数成员变量简单赋值
总结:只要你写了构造函数,那么你必须用。
#include <iostream> using namespace std;class Test { public:// Test(const Test& obj) //copy构造函数 作用: 用一个对象初始化另外一个对象 // { // a = obj.a + 100; // b = obj.b + 100; // }// Test(int _a, int _b) // { // ; // } Test(){cout<<"hi"<<endl;}void printT(){cout << "a:" << a << "b: "<<b<< endl; }private:int a;int b; };//当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数 //当类中定义了有参数构造函数是,c++编译器不会提供无参数构造函数//在定义类时, 只要你写了构造函数,则必须要用int main() {Test t1; //调用无参构造函数cout<<"hello..."<<endl;system("pause");return 0; }
P187 深浅拷贝问题
#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std;class Name { public:Name (const char *myp){m_len=strlen(myp);m_p=(char *)malloc(m_len+1);// strcpy(m_p,myp);}//解决方案:手工的编写拷贝构造函数 使用深copyName(const Name& obj1){m_len=obj1.m_len;m_p=(char *)malloc(m_len+1);strcpy(m_p,obj1.m_p);}~Name(){if (m_p!=NULL){free(m_p);m_p=NULL;m_len=0;}} private:char *m_p;int m_len; };void objpalymain() {Name obj1("abcde");Name obj2=obj1;//默认的拷贝构造函数 C++编译器提供的 }void main() {objpalymain();system("pause"); }
P188 深拷贝和浅拷贝-默认的等号操作符也是浅拷贝
#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std;class Name { public:Name (const char *myp){m_len=strlen(myp);m_p=(char *)malloc(m_len+1);// strcpy(m_p,myp);}//解决方案:手工的编写拷贝构造函数 使用深copyName(const Name& obj1){m_len=obj1.m_len;m_p=(char *)malloc(m_len+1);strcpy(m_p,obj1.m_p);}~Name(){if (m_p!=NULL){free(m_p);m_p=NULL;m_len=0;}} private:char *m_p;int m_len; };void objpalymain() {Name obj1("abcde");//Name obj2=obj1;//默认的拷贝构造函数 C++编译器提供的 Name obj3("obj3");obj3 = obj1;//等号操作 }void main() {objpalymain();system("pause"); }
P189 构造函数的初始化列表
1、对象初始化列表
1)对象初始化列表出现原因
1.必须这样做:
如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,
如果没有初始化列表,那么他将无法完成第一步,就会报错。
2.类成员中若有const修饰,必须在对象初始化的时候,给const int m 赋值
当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,
因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。
2)C++中提供初始化列表对成员变量进行初始化
语法规则
Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
{
// some other assignment operation
}
3)注意概念
初始化:被初始化的对象正在创建
赋值:被赋值的对象已经存在
4)注意:
成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
初始化列表先于构造函数的函数体执行
#include<iostream>
using namespace std;class A { public:A(int _a){a=_a;cout<<"构造函数"<<"a:"<<a<<endl;}~A(){cout<<"A的析构函数"<<"a:"<<a<<endl;} protected: private:int a; }; //1 构造函数的初始化列表 解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数) // 根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A // 新的语法 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3) class B { public:B(int _b1,int _b2):a1(1),a2(2),c(0){b1=_b1;b2=_b2;}B(int _b1,int _b2,int m,int n):a1(m),a2(n),c(0){b1=_b1;b2=_b2;cout<<"B的构造函数"<<endl;}~B(){cout<<"B的析构函数"<<endl;} protected: private:int b1;int b2;A a1;A a2;const int c;
};//2 先执行 被组合对象的构造函数 // 如果组合对象有多个,按照定义顺序, 而不是按照初始化列表的顺序// 析构函数 : 和构造函数的调用顺序相反//3 被组合对象的构造顺序 与定义顺序有关系 ,与初始化列表的顺序没有关系 //4 初始化列表 用来 给const 属性赋值 void obj10play() {//A a1(10);//1. 参数传递B objB(1,2,3,4);//2. 调用顺序 } void main() {obj10play();system("pause"); }
输出结果
P190 强化训练1--构造和析构调用顺序
//对象做函数参数 //1 研究拷贝构造 //2 研究构造函数,析构函数的调用顺序//总结 构造和析构的调用顺序 #include "iostream" 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;} 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);//实参调用doThing函数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();system("pause");return 0; }输出结果
P191 强化训练2--匿名对象声明周期
//研究构造函数,析构函数的调用顺序 //总结 构造和析构的调用顺序 #include "iostream" 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;} private:int a;int b;int c; }; 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() { run3();system("pause");return 0; }
第一种 ABCD(400, 500, 600)输出结果 -- 先调用构造函数然后紧接着调用析构函数:因为是匿名对象,没有人接管它
第二种 ABCD abcd = ABCD(100, 200, 300);输出结果 -- 匿名对象转正abcd,运行完毕之后会调用析构函数
P192 强化训练3--构造中调用构造(产生匿名对象)
#include "iostream" 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\n", t1.getC()); //请问c的值是?system("pause");return 0; }
输出结果:
由上节可知,匿名对象的生命周期先调用构造函数然后紧接着调用析构函数:因为是匿名对象,没有人接管它
匿名对象和t1没有关系,调用的是匿名对象的析构函数
P193 new和delete的基本语法
1)在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。
C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。
注意: new和delete是运算符,不是函数,因此执行效率高。
2)虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。
new运算符的例子:
new int; //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针) new int(100); //开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址 new char[10]; //开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址 new int[5][4]; //开辟一个存放二维整型数组(大小为5*4)的空间,返回首元素的地址 float *p=new float (3.14159); //开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p
3)new和delete运算符使用的一般格式为:
用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。
4) 应用举例
////分配基础类型 void main() {// int *p = (int *)malloc(sizeof(int));*p = 10;free(p);int *p2 = new int; //分配基础类型*p2 = 20;free(p2);// int *p3 = new int(30);printf("*p3:%d \n", *p3);//30delete p3;cout<<"hello..."<<endl;system("pause");return ; }
//分配数组变量 void main() {//c语言分配数组int *p = (int *)malloc(sizeof(int) * 10); //int array[10];p[0] = 1;free(p);//c++分配数组 int *pArray = new int[10] ;pArray[1] = 2;delete [] pArray; //数组不要把[] 忘记char *pArray2 = new char[25] ; //char buf[25]delete [] pArray2;cout<<"hello..."<<endl;system("pause");return ; }
class Test { public:Test(int _a){a = _a;cout<<"构造函数执行" <<endl;}~Test(){cout<<"析构函数执行" <<endl;}protected: private:int a; };//分配对象new delete //相同 和 不同的地方 new能执行类型构造函数 delete操作符 能执行类的析构函数 void main() {//c Test *pT1 = (Test *)malloc(sizeof(Test));free(pT1);//c++Test *pT2 = new Test(10);delete pT2;cout<<"hello..."<<endl;system("pause"); }
P194 new和delete深入分析
混用测试、异同比较
结论: malloc不会调用类的构造函数
Free不会调用类的析构函数
#include <iostream> using namespace std;// 1 // malloc free c语言的函数 // new delete 操作符 c++的语法//2 new 基础类型变量 分配数组变量 分配类对象//3 ////分配基础类型--混搭没有问题 void main01() {// int *p = (int *)malloc(sizeof(int));*p = 10;//free(p);delete p;int *p2 = new int; //分配基础类型*p2 = 20;free(p2);// int *p3 = new int(30);printf("*p3:%d \n", *p3);//delete p3;free(p3);cout<<"hello..."<<endl;system("pause");return ; }//分配数组变量 void main02() {//c语言分配数组int *p = (int *)malloc(sizeof(int) * 10); //int array[10];p[0] = 1;//free(p);delete[] p;//c++分配数组 int *pArray = new int[10] ;pArray[1] = 2;//delete [] pArray; //数组不要把[] 忘记free(pArray);char *pArray2 = new char[25] ; //char buf[25]delete [] pArray2;cout<<"hello..."<<endl;system("pause");return ; }class Test { public:Test(int _a){a = _a;cout<<"构造函数执行" <<endl;}~Test(){cout<<"析构函数执行" <<endl;}protected: private:int a; };//分配对象new delete //相同 和 不同的地方 new能执行类型构造函数 delete操作符 能执行类的析构函数// malloc free 函数 C 只会分配内存大小 不会调用类的构造析构函数 // new delete 操作符号 c++的关键字 //结论 void main() {//c Test *pT1 = (Test *)malloc(sizeof(Test));//free(pT1);delete pT1;//c++Test *pT2 = new Test(10);//delete pT2;free(pT2);cout<<"hello..."<<endl;system("pause"); }
P195 静态成员变量和静态成员函数
1)定义静态成员变量
- 关键字 static 可以用于说明一个类的成员,
静态成员提供了一个同类对象的共享机制
- 把一个类的成员说明为 static 时,这个类无论有多少个对象被创建,这些对象共享这个 static 成员
- 静态成员局部于类,它不是对象成员
2)静态成员函数
1)概念
- 静态成员函数数冠以关键字static
- 静态成员函数提供不依赖于类数据结构的共同操作,它没有this指针
- 在类外调用静态成员函数用 “类名 :: ”作限定词,或通过对象调用
2)案例
#include<iostream> using namespace std;class BB { public:void printC(){cout<<"c:"<<c<<endl;}void AddC(){c=c+1; //成员函数访问静态数据成员 }static void getC() //静态成员函数 {cout<<"c:"<<c<<endl;//请在静态成员函数中,能调用 普通成员属性 或者 普通成员函数吗?cout<<"a:"<<a<<endl; //error C2597: 对非静态成员“BB::a”的非法引用 } private:int a;int b;static int c;//声明与定义静态数据成员 };int BB::c=10;//声明与定义静态数据成员void main() {BB b1,b2,b3;b1.printC();//10b2.AddC();//11b3.printC();//11//静态成员函数调用的方法b3.getC();//用对象 BB::getC();system("pause");//类:: }
static中取a的值,不确定是哪个对象的a,对非静态成员“BB::a”的非法引用
static中取c的值c,c是个静态成员,静态成员是属于整个类的
在静态成员函数中只能使用静态成员变量,不能使用普通成员函数
** C++编译器是如何支持面向对象的机制的
P196 C++面向对象模型初探
基础知识
C++中的class从面向对象理论出发,将变量(属性)和函数(方法)集中定义在一起,用于描述现实世界中的类。从计算机的角度,程序依然由数据段和代码段构成。
C++编译器如何完成面向对象理论到计算机程序的转化?
换句话:C++编译器是如何管理类、对象、类和对象之间的关系
具体的说:具体对象调用类中的方法,那c++编译器是如何区分,是那个具体的类,调用这个方法那?
#include "iostream"using namespace std;class C1 { public:int i; //4int j; //4int k; //4 protected: private: }; //12class C2 { public:int i; int j; int k; static int m; //4 public:int getK() const { return k; } //4void setK(int val) { k = val; } //4protected: private: }; //24 16 12(铁钉的不对)struct S1 {int i;int j;int k; }; //12struct S2 {int i;int j;int k;static int m; }; //16int main() {printf("c1:%d \n", sizeof(C1));//12printf("c2:%d \n", sizeof(C2));//12printf("s1:%d \n", sizeof(S1));//12printf("s2:%d \n", sizeof(S2));//12 system("pause"); }
1)C++类对象中的成员变量和成员函数是分开存储的
成员变量:
普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对齐方式
静态成员变量:存储于全局数据区中
成员函数:存储于代码段中。
问题出来了:很多对象共用一块代码?代码是如何区分具体对象的那?
换句话说:int getK() const { return k; },代码是如何区分,具体obj1、obj2、obj3对象的k值?
1、C++类对象中的成员变量和成员函数是分开存储的。C语言中的内存四区模型仍然有效!
2、C++中类的普通成员函数都隐式包含一个指向当前对象的this指针。
3、静态成员函数、成员变量属于类
静态成员函数与普通成员函数的区别
静态成员函数不包含指向具体对象的指针
普通成员函数包含一个指向具体对象的指针
P197 this指针
#include <iostream> using namespace std;class Test { public:Test(int a, int b) //---> Test(Test *this, int a, int b) {this->a = a;//this就是t1取地址,谁调用它它就是谁this->b = b; }void printT(){cout<<"a: " <<a <<endl;cout<< "b: " << this->b <<endl;} protected: private:int a;int b; };void main() {Test t1(1, 2);t1.printT();// ===> printT(&t1)cout<<"hello..."<<endl;system("pause");return ; }
转载于:https://www.cnblogs.com/yangyuqing/p/10408219.html
总结
以上是生活随笔为你收集整理的C++--day06的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 微服务实战:从架构到发布(二)
- 下一篇: springMVC之Intercepto