C++学习总结和归纳.docx

上传人:牧羊曲112 文档编号:3153677 上传时间:2023-03-11 格式:DOCX 页数:40 大小:54.39KB
返回 下载 相关 举报
C++学习总结和归纳.docx_第1页
第1页 / 共40页
C++学习总结和归纳.docx_第2页
第2页 / 共40页
C++学习总结和归纳.docx_第3页
第3页 / 共40页
C++学习总结和归纳.docx_第4页
第4页 / 共40页
C++学习总结和归纳.docx_第5页
第5页 / 共40页
亲,该文档总共40页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《C++学习总结和归纳.docx》由会员分享,可在线阅读,更多相关《C++学习总结和归纳.docx(40页珍藏版)》请在三一办公上搜索。

1、C+学习总结和归纳面向对象技术基础 1构造函数和析构函数和特殊成员 构造函数的作用:PS:构造函数可以在类外定义 1.给对象一个标识符 2.为对象数据成员开辟内存空间 3.完成对象数据成员的初始化 4.如果没有显示定义构造函数,则默认构造函数只完成1、2两步 5.初始化表达式: 1.可用于初始化类的任意数据成员,static数据成员除外 2. 只能用初始化表初始化基类,const数据成员,引用成员,类对象成员 3. Const数据成员,只能在初始化表中初始化,必须显示的定义构造函数,而且要注意类中const整型数据成员不能作为常量指明一个数组的大小 复制构造函数: 1.原型:Point:Poi

2、nt; 2.调用:Point pt2 = pt1; 或者 Point pt2(pt1); 3.默认复制构造函数:将源对象中的成员按“内存 单元复制”的方式复制到目的对象中,复制后,源对象和目的对象除了地址不同外,各个成员的取值都是相同的,特别是对于含有指针类型的数据成员,指向的内存单元相同 4.显示定义复制构造函数: 1. 类中含有指针型的数据成员,需要使用动态内存的,需要显示定义 2. 自定义复制构造函数,也可以使用初始化表来给目的对象赋值 构造函数注意点: 1.如果类中有类对象成员,那么该对象所在的类中必须定义了无参构造函数或所有参数都有默认值的构造函数,或默认构造函数,否则不合法 2.只

3、要自定义了构造函数,则编译器不会提供默认构造函数 Static数据成员和static成员函数 1.static 数据成员在编译的时候就被创建,此时仅知道如何分配内存,但是必须在类外进行初始化 2.类定义:Class computer private: static float total_price; 3.在cpp文件中初始化(不能再使用static关键字):float computer:total_price = 0; 4.静态成员函数只能调用静态成员变量,但是可以在函数中创建一个本类对象,也可以delete 一个本类对象 5.如果静态数据成员使用const修饰,而且是整型,浮点类型、布尔类

4、型或枚举型,但不能是类对象,数组、引用和指针,C+允许该成员在类定义中初始化,此时不能再外部再次对该静态成员进行定义性声明,但可以对该成员进行引用性声明 6.定义性声明:指在一个文件中定义一个全局变量,如 int a; 7.引用性声明:指当该文件需要使用另一个文件的某个变量时,则需要使用引用性声明这个是另一个文件中的全局变量:external int a; Const 对象和Const成员函数 1.const 成员函数无法修改数据成员,否则编译器报错 2.定义:void print( ) const 函数体内不能修改数据成员 3.能作用于const对象的成员函数除了构造函数和析构函数外,就只有

5、const成员函数了,因为const对象只能被创建,撤销以及只读访问。即:const对象只能调用const成员函数 对象的大小: 1.对象在内存中以结构的形式,存放在数据段中或堆中 2.Sizeof,计算的是所有非static成员的大小,注意还要保证数据对齐(填充),引用成员当指针来维护,类中有虚函数还需要额外分配一个指针指向虚函数表 3.Static成员变量在编译期就已经在静态存储区分配了内存,在程序整个运行期间 4.类中的成员函数存在于代码段中,一个类只有一个副本 this指针: 1.this指针隐含在成员函数内的一种指针,指向本对象的指针 2.this 指针和对象同生共死,具有类作用域

6、3.主要作用:a.显示指明类中数据成员;b.返回本对象的指针或引用,也无法通过指针this指针修改该对象 构造函数允许按参数默认方式调用(带有多个默认值得构造函数) classA private: int x;int y; public: A(intxyp = 0) x = xyp;y = xyp; cout 一个默认值的构造函数被执行 endl; 如果没有默认值的情况下,则A a(1);编译 A(intxp=0 , intyp=0) 能通过,调用的是第一个构造函数 x = xp;y = yp; cout 两个默认值的构造函数被执行print;p+; 来访问则delete p; p = NUL

7、L; 操作会出错 2. delete p; 释放了数组所占的内存空间,new和delete激活了数组中每个对象的构造函数和析构函数 3. malloc和free不能为对象动态的申请内存,因为无法像new/delete或new/deltete那样自动调用对象的构造函数和析构函数 4. delete p;操作后p指针不会被销毁,而且指向原来的地方,应及时的置NULL 内存空间结构 友元和重载 2类作用域和可见域 类的作用域 1.作用域分类:类作用域、类名作用域、对象的作用域 2.类作用域:类中定义的数据成员和函数成员的作用域是整个类,在类中是可见的,在类外是不可见的 3.类作用域意味着不能从外部直

8、接访问类的任何成员,即使该成员的访问权是public,也要通过对象名来调用,对于static成员,要指定类名来调用 4.发生屏蔽现象,此时借助this指针或类名:形式指明访问的类成员 5.使用:x,可以访问全局变量,可以在程序的任何地方访问全局变量 6.全局作用域:在函数和其他类定义的外部所定义的类为全局类,绝大多数C+类是定义在该作用域中,这些类具有全局作用域 7.类作用域:嵌套类且是定义的访问权限为public,则该嵌套类和外类作用域相同,只是必须使用外类:内类 的形式来访问内类的成员或创建对象;如果定义的权限是private时,则只能在外部类定义中使用类名来创建该嵌套类的对象 8.块作用

9、域:类的定义在代码块中,则该类为局部类,具有块作用域。局部类必须完全定义在局部作用域内,所以它的所有成员函数必须是内联的,因为函数内不能再定义函数 对象的生存期、作用域和可见域 1.对象的生存期也是对象中所有非静态成员的生存期,对象的所有非静态数据成员都随着对象的创建而创建和初始化,随着对象的撤销而撤销 2.对象的生存期、作用域和可见域取决于对象的创建位置,同样有全局、局部和类内之分,同普通变量并无区别 先定义,后实例化 可以先创建指向类型B对象的指针或引用 不能这样创建对象 Class B; / 声明 Class B; / 声明 B* pB = NULL; /创建B类对象指针 B objec

10、tB; /创建B类对象 Class B ; Class B ; 对象内存的释放或堆内存 1.通过 B* pB = new B; 一个对象来创建一个对象的,在使用完对象后需delete pB; 2.通过 B*pB = new B2; 创建了一个对象数组,在使用完后需delete pB; 3.在对象创建时和函数执行中通过new和malloc申请的动态内存只能通过显示的用delete或free释放,申请的动态内存不会随着对象的撤销而撤销,在程序结束时,操作系统会回收程序所开辟的所有内存,但要养成及时释放内存的习惯 友元的成员函数 classpoint; 必须先声明point类,因为line类必须先定

11、义再定义point类 classline public: float dis(point&p1, point&p2);/类成员函数原型 ; classpoint 友元函数原型所在的类要 先定义才能定义point类 private: int x, y; friendfloatline:dis(point&p1, point&p2);/友元的声明 public: point(inti = 0, intj = 0)x = i;y = j; ; int main line line1; point p1(1, 2), p2(4, 5); cout line1.dis(p1, p2)x = a;p1-y

12、 = b; ; ; 3友元 非成员形式的友元函数 1.定义:如果在某个类的定义中用friend声明了一个外部函数后,则这个外部函数即为该类的友元函数 2.非成员的友元函数是为了让外部函数也能访问类的私有成员 classpoint private: int x, y; friendfloat dis(point&p1, point&p2); public: point(inti=0,intj=0)x = i;y = j; ; int main point p1(1, 2), p2(4, 5); coutdis(p1,p2),* 2.不能重载的运算符:. (成员运算符),.*(指向成员的指针),:

13、,?:,sizeof 3.只能以成员函数形式重载的运算符:,=,函数调用运算符:,- 4.重载原则:不改变优先级和结合性,不改变语法结构,一般不改变语义 5.用户自定义的重载运算符,要能访问对象的private成员的,因此运算符重载有成员函数和友元函数两种形式 6.重载运算符时要注意各运算符之间的关联:,*,&,-要同时被重载 成员函数形式:complex operator + (const complex & ); 友元函数形式:friend complex operator + (const complex &,const complex &); 构造函数:complex(double r

14、= 0.0,double i = 0.0) real = r; imag = i; 成员函数形式: 友元形式: Complex c1(1.0,2.0),cRes; Complex c1(1.0,2.0),cRes; cRes = 5 + c1; /但cRes = c1 + 5;合法 cRes = c1 + 5; 编译器解释:cRes = 5.operator+(c1); 编译器解释:cRes = operator+(5,c1); 编译器根据合适的构造函数将5转换此时,5不能调用complex成员函数, 为complex类临时对象,完成参数匹配 编译器报错 总结:1.友元函数更容易实现类型装换,

15、使两个操作符都被当成函数的参数 2.如果程序中有大量用到complex类对象和内置类型相加操作,最好的方式重载几个complex类和内置数据类型相加的版本 以成员函数形式重载 以友元函数形式重载 1.先在类定义中声明:返回类型 operator 运算符 1.先在类定义中声明友元:friend 返回类型 operator 运算符 2.在类外定义:返回类型 类名:operator 运算符 2.在类外定义:返回类型 operator 运算符 重载前置+:(可以作为左值) 重载后置+: 重载前置+:(可以作为左值) 重载后置+: friend complex& operator +(complex&)

16、; friend complex operator +(complex&, int); complex& operator +; complex operator +; complex& complex:operator +(complex& c1) complex complex:operator +(complex& c1,int) complex& complex:operator + complex complex:operator +(int) c1.real+=1; c1.imag += 1; complex ctemp = c1; +c1; real+=1; imag += 1;

17、 complex ctemp = *this; +(*this); return c1;/返回实参引用 return ctemp;/返回一个临时对象 return (*this);/返回自身引用 return ctemp;/返回一个临时对象 +对象解释为:operator +(对象); 对象+解释为:operator +(对象,0); +对象解释为:对象.operator +; 对象+解释为:对象.operator +(0); 重载赋值运算符 重载函数调用运算符 computer(constcomputer&cp) 含有指针成员的复制构造函数需显示定义 brand = newcharstrle

18、n(cp.brand) + 1; strcpy(brand, cp.brand);price = cp.price; computer&computer:operator=(constcomputer&cp) 赋值运算符重载解决 if (this = &cp)return (*this); 有指针类型的数据成 price = cp.price;delete brand; 员的类对象之间赋值 brand = newcharstrlen(cp.brand) + 1; if (brand != NULL)strcpy(brand, cp.brand); return (*this); 返回的是当前对

19、象的引用,为实现链式赋值 computer com1(Dell, 2000); 调用的是复制构造函数,很好的解决了诸如:computer com2 = com1; computer com2=com1或computer com2(com1) 等形式的指针赋值 computer com3; 先调用的是无参构造函数创建com3对象,在调用赋值运算符,com3 = com1; 如果无重载时,则直接值赋值,无法开辟新的内存出错 重载下标运算符 classDemo public: double operator(double x, double y); double operator(double x,

20、 double y, double z); ; doubleDemo:operator(doublex, doubley) returnxy ? x : y; doubleDemo:operator(doublex, doubley, doublez) return (x + y)*z; Demo de; 如果一个类重载了函数调用运算符operator ,则可以将该类de(2.5, 0.2); 对象作为一个函数使用 类中可以定义多个函数调用运算符的重载版本,根据调用时de(1.2, 1.5, 7.0); 参数的不同调用对应的调用运算符。 由其他类型向定义类的转换 假设由A类型向自定义B类型转换

21、 PS构:point(int x=0,int y=0) ; point(anotherpoint ap) 1. 只有一个B; /point p1; anotherpoint p2; p1 = p2; 则等价于p1 = point(p2) 2. 包含一个B(A a,.); 其他参数都有默认值 / double x= 1.0; p1 = x; 等价于 p1 = point(int(x),0) 3. 不包含A类型但包括C类型(double),没有其他参数或其他都有默认值,则A隐转C 由定义类向其他类型的转换(通过转换函数实现) 1.转换函数必须是成员函数形式,没有参数 1.转换函数定义:operat

22、or 目标类型名; 2.不能指定返回类型,但函数体内需return 目标类型的变量 2.定义了转化函数后,转化都是隐式完成。 3.也可以通过显示调用:int x = int(p1); anotherpoint p2 = anotherpoint(p1); 4.隐式转化带来的二义性:coutp1;因无法知道p1隐式转为int型还是double型 5.解决二义性:a. 显示转换为int型;b.在point类中重载操作符整体提升转换其他转换用户自定义的类型转换 complex c1(1.0,2.0) ,cRes; cRes = c1+5; 方案1:cRes = complex(double(c1)+

23、5 , 根据规则:编译器选择方案1执行; 0); 方案2:cRes = c1 + complex(5,0); 通过隐式转换: classpoint point p1(4, 5); private: int xpos; int ypos; public: intx1 = p1; point(intx = 0, inty = 0)xpos = x; ypos = y; doubledx = p1; operatordouble return xpos*ypos; anotherpoint p2; P2 = p1;/隐式转换 operatorint return xpos; Coutp1;/出现二义

24、性 operatoranotherpoint returnanotherpoint(xpos, ypos); 1.重载函数原型: 返回类型& operator (参数类型) PS:参数类型默认为int 2.Computer com3; 则com1 等价于com.operator(1); classCharSZ 参数只能是一个,类型没有限制,如果private: 是非int类型,则需重载下标运算符 int Len;char* pBuf; public: CharSZ(intl) Len = l; pBuf = newcharLen; CharSZ delete pBuf; 1.对CharSZ类的

25、下标运算符进行了重 int GetLen return Len; 载,以后便可以对普通CharSZ对象使用运算符 ; 2.返回类型为引用,返回值可作左值 char&CharSZ:operator(inti) staticchar def = 0;/定义为局部静态变量,当越界时返回空字符引用 if (i= 0) return pBufi; 则不 else cout 下标越界 endl; return def; 返回的是引用,能定义为局部变量 char* sz = Helloo;CharSZ de(7); for (cnt = 0; cnt strlen(sz) + 1; cnt+)decnt =

26、 szcnt; for (cnt = 0; cnt de.GetLen; cnt+)cout decnt; 继承 5共同基类派生和多基派生二义性 多基派生产生的二义性: A print B print C disp print 编译器不知道Print是从哪个 基类中继承而来产生二义性 解决方案: 使用成员名限定来消除二义性:void disp A : porint; 共同基类带来的二义性: A SetX , print A B D 出现二义性原因: 类B类C都是从类A派生过来,都有一份类A的复制,当B、C类派生出类D时,编译器无法知道所调用的函数继承自B还是C,即出现二义性 解决方案: 使用关

27、键字virtual将共同基类A声明为虚基类,类B、C由类A虚基派生而来 class B : virtual public A ; class C: virtual public A ; class D : public B,public C ; 虚基派生类的构造函数和析构函数 classA private: int x; public: A(intxp) x = xp; cout A类的构造函数被执行 endl; ; classB :virtualpublicA public: B(intxp) :A(xp) cout B的构造函数被执行 endl; ; 类B,C都可以创建实例对象,因此必须在

28、classC :virtualpublicA 类B和类C的构造函数中调用基类A的构造 函数 public: C(intxp) :A(xp) cout C类的构造函数被执行 endl; ; classD :publicB, publicC public: D(intxp) :A(xp), B(xp), C(xp) cout D类的构造函数被执行 endl; ; 创建D类对象时,D类的两个基类B和C类构造函数都被int main 调用,但是B和C类构造函数的初始化表中对虚基类A 执行的是D类构造函数初始化表 D expD(2); 构造函数的调用被忽略,中列出的虚基类A的构造函数 6派生类的构造函数

29、和析构函数 派生类的构造函数实现的功能: 1.完成对象所占整块内存的开辟,由系统在调用构造函数时自动完成 2.在初始化表调用基类的构造函数完成基类成员的初始化 3.若派生类中含有对象成员,const成员或引用成员,则必须在初始化表中完成 4.执行派生类构造函数体 classA private: int x; public: A(intxp = 0) x = xp;cout A的构造函数被执行 endl; ; classB public: Bcout B的构造函数被执行 endl; ; classC :publicA private: 基类构造函数和对象成员b只能在派生类 int y; 初始化表

30、中初始化,根据构造函数可省略 B b; public: 多基派生时初始化顺序是根据定义 C(intxp, intyp) :A(xp),b 派生类时指定的派生类顺序决定的 y = yp; cout C类的构造函数被执行 endl; ; int main C expC(1,2); return 0; 7基类于派生类对象间的相互转换 类型适应 1、赋值转换:用派生类对象为基类对象赋值,用派生类对象初始化基类对象指针 2、指针转换:将派生类指针赋值给基类指针,但是将基类指针赋值给派生类对象指针时必须进行强制转换 3、虚基类情况: 1. 合法:pA = &d; a = d; A& rA = d; 2.不

31、合法:D* ppD = (D*)(B*)pA; PS:相反的转换是不可以的即使添加了全部路径 共同基类多基派生对象的内存分布图 Class A Class B :public A Class C :public A Class D :public B,public C 出现二义性: A = d; 属于隐式转换 pA = pD; #include usingnamespace std; classBase public: Base(intxp=0) x = xp; cout Base构造函数被执行 endl; private: int x; ; classChild :publicBase pr

32、ivate: int y; public: Child(intxp=0, intyp=0) :Base(xp) y = yp; 克服二义性: cout Child类构造函数被执行 endl; 1、对指针显示指明路径:pA = (A*)(B*) pD; 2、先将指针转到不会产生二义性的类型:pA = (B*)pD; void disp cout y endl; 3、对象赋值采用强制类型转换:a = B(d); 或者C语言中:a = (B)b; cout Child派生类添加的新成员函数被执行disp; 的内存是无效的,但编译运行未报错 Base* pBase = newChild(1,2); p

33、Child = (Child*)pBase; 派生类内存有效,基类指针强转为派生 pChild-disp; 类指针后,可以访问到派生类的新成员 system(pause); return 0; 多态 8多基派生 #include usingnamespace std; classA public: virtualvoid a cout a in A endl; virtualvoid b cout b in A endl; virtualvoid c cout c in A endl; ; classB public: virtualvoid a cout a in B endl; virtu

34、alvoid b cout b in B endl; void c cout c in B endl; void d cout d in B endl; ; classC :publicA, publicB public: 由于父类中都是虚函数,则函数 a一定是虚函数,故virtual可省 virtualvoid a /虚函数,覆盖 cout a in C endl; void c /特殊,重定义 cout c in C endl; void d /非虚函数,隐藏 cout d in Ca;/由于a在父类中都是虚函数,且在子类中进行了覆盖,则符合动态联编,故输出在C类中 pb-a;/由于a在父

35、类中都是虚函数,且在子类中进行了覆盖,则符合动态联编,故输出在C类中 coutb;/由于b在父类A中是虚函数,但是在子类C中并没有覆盖,则只能是静态联编,故输出在A类中 pb-b;/由于b在父类B中是虚函数,但是在子类C中并没有覆盖,则只能是静态联编,故输出在B类中 coutc;/由于c函数在父类A中是虚函数,则会根据指针指向的对象来找具体函数,故输出在C类中 pb-c;/由于c在父类B中是普通函数,则只能根据pb的指针类型来找具体函数,故输出在B类中 coutd;/非虚函数,只能根据指针类型来找到对应的函数,故输出的是在B类中 输出: 注释:黄黑代表的是虚函数 a in C 多基派生: a

36、in C 1.先看子类是否重定义了:b没有重定义,则是静态联编,根据指针类型找函数 2.再看基类是否是虚函数:B类c不是虚函数,则是静态联编,A类c是虚函数,b in A 则为动态联编 b in B 3.注意隐藏和覆盖重载之间的区别:C类中的a覆盖了A、B类中的a函数,C类 中的c函数是重定义了A、B类的c函数,C类中的d隐藏了B类dc in C 函数,即是对普通函数来说是隐藏,对虚函数是覆盖 c in B PS: 覆盖必须保证基类是虚函数。 d in C d in B Press any key to continue A a, b, c, B a, b, c, d C a, c, d 9虚

37、基派生(共同基类和虚继承) 继承关系:ps: 虚继承保证最派生类只有共同基类的一个副本 Class A a, b, c, d ;/定义的都是虚函数 Class B:virtual public A a, b ; / 继承父类函数都是虚函数 Class C: virtual public A a, b, c ; Class D: public B, public C a, b ; /a,b函数一定要在D类中重定义,否则编译不通过 实例对象: A* pA = new D; pA - a; pA - b; pA - c; pA - d; 注释:黄黑代表的是虚函数 菱形结构派生:一定是虚基派生,保证了A类中的函数在D类中只有一份复制 A a, b, c, d 输出:a in D ,在D类中进行了重定义,根据具体的对象找函数 输出:b in D ,b在类D中必须重定义,否则在类D中继承而来的b函数不知道是来自类B还是类C,D类中的b是继承B、C中的b #include using namespa

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号