文章目录码字不易,万字总结欢迎收藏,如果觉得有帮助的话求赞转发关注~
有不当之处,还请评论区指正,帮助完善文章~感激不尽
逢考必过~高分喷雾✅面试好运,祝一切顺利!
- C++面向对象程序设计总复习
- 1.构造函数汇总:
- 2.析构函数&访问对象方式&对象指针&对象数组:
- 3.类对象作为数据成员:
- 4.const关键字:
- 1.常对象:
- 2.常成员函数:
- 3.常数据成员:
- 4.指向对象的常指针:
- 5.指向常对象的指针:
- 6.区分指向对象的常指针&指向常对象的指针:
- 7.对象的常引用:
- 5.static关键字:
- 1.静态数据成员:
- 2.静态成员函数:
- 6.内联函数inline:
- 7.友元函数&友元类:
- 1.友元函数:
- 2.友元类:
- 8.继承:
- 1.语法:
- 2.菱形继承:
- 9.多态:
- 1.C++多态方式:
- 2.虚函数详解:
- 3.虚函数使用注意事项:
- 10.运算符重载:
- 1.重载运算符常用的两种方式:
- 11.模板template:
- 12.文件读写:
- 1.写文件的步骤:
- 2.读文件的步骤:
- 13.“八股文”系列:
- 1.面向过程和面向对象的区别:
- 2.c++的三大特性:
- 3.C++ 重载和重写的区别:
- 14.《C++面向对象程序设计谭浩强》阅读笔记:
- 1.part1(零散知识点&模板&setw&内联&new&delete):
- 2.part2(全局函数为友元&.h文件.cpp文件):
- 3.part3(友元成员函数):
- 4.part4(重载正负号以及流提取运算符):
- 5.part5(虚基类):
- 6.part6(文件操作):
- 7.part7(异常处理throw&try&catch):
- 15.期末考试之概念题目汇总(已经过筛选):
- 16.流对象成员函数补充:
//默认的构造函数
Date();
//带默认值的构造函数
Date() {
year = 2020;
month = 2;
day = 14;
}
//带参构造函数
Date(int y, int m, int d) {
year = y;
month = m;
day = d;
}
//初始化表
Date(int y,int m,int d):year(y),month(m),day(d){}
//结合带默认值的带参构造
Date(int y=2021, int m=5, int d=20) {
year = y;
month = m;
day = d;
}
//带默认值的初始化表
Date(int y=2022,int m=6,int d=10):year(y),month(m),day(d){}
//拷贝构造函数
//Date test2=test1;时发生拷贝构造,而Date test2; test2=test1;则是先构造再赋值,并不调用拷贝构造函数
Date(const Date &t) {
year = t.year;
month = t.month;
day = t.day;
}
2.析构函数&访问对象方式&对象指针&对象数组:
#include3.类对象作为数据成员:using namespace std; //构造函数与析构函数: class Date { public: //带默认值的初始化表 Date(int y=2022,int m=6,int d=15):year(y),month(m),day(d){ cout << "构造函数已被调用" << endl; } //析构函数 ~Date() { cout << "析构函数已被调用" << endl; } //显示日期 void show(); private: int year, month, day; }; //在类外定义成员函数 void Date::show() { cout << year << " " << month << " " << day << endl; } int main() { Date test1(2022, 12, 25); //对象的访问方式: test1.show(); Date *p; //定义一个对象指针 p = &test1; //让这个对象指针指向test1对象 p->show(); //通过对象指针访问成员 (*p).show(); //相当于test1.show() //对象数组:数组中存储多个对象 Date dates[3] = { Date(2022,5,20),Date(2022,5,21),Date(2022,5,22) }; dates[1].show(); //2022 5 21 //引用:相当于别名,引用起来的二者是等同的 //语法:数据类型 &别名 = 原名 Date &tt1 = test1; //使用new动态申请一个对象 Date *q = new Date(2022, 12, 19); q->show(); delete q; //使用new动态申请的空间,使用完之后应该delete释放这片空间 }
即一个类的对象作为另一个类的成员:
- 其他类对象作为本类数据成员时,其他类对象构造函数先调用,再调用本类构造函数,析构时,析构函数调用顺序相反,(变量存放在栈区空间,先申请的在下面,后申请的在上面,释放时先释放上面,后释放下面)(即先进后出)
#include4.const关键字: 1.常对象:#include using namespace std; //定义一个日期类 class Date { public: Date(int y=2005,int m=6,int d=19):year(y),month(m),day(d){} void show() { cout << year << " " << month << " " << day << endl; } private: int year, month, day; }; //定义一个Person类 class Person { public: //构造函数示例1 Person(string n,char g,int a,int y,int m,int d):name(n),gender(g),age(a),birth(y,m,d){} //构造函数示例2 void show() { cout << name << " " << gender << " " << age << endl; birth.show();//在这个程序里,出现两个同名函数show,将发生函数重载 } private: string name; char gender; //w:女(women),m:男(men) int age; Date birth; //类对象作为另一个类的数据成员 }; int main() { Person p1("lena", 'w', 18, 2006, 6, 19); //注意c++里双引号单引号不能乱用 p1.show(); }
-
使用const修饰的对象称为常对象,在定义时必须初始化,且此后不能更改
-
定义格式如下:
- 类名 const 对象名(实参表列) Date const test1(2022,2,14);
- const 类名 对象名(实参表列)const Date test1(2022,2,14);
-
常对象只能调用const类型的成员函数,不能调用非const类型的成员函数,这是为了防止通过调用成员函数改变了常对象中数据成员的值
-
定义格式如下:
- 函数返回类型 成员函数名(参数表列)const { 函数体 }
-
const放在函数参数表列的后面,const是函数类型的一部分,可以与不带const的函数进行重载,当同名的一般函数和常成员函数同时存在时,一般对象调用一般的成员函数,常对象调用常成员函数
-
声明函数以及定义函数都要有const修饰
-
常对象只能调用const修饰的函数,即常成员函数
-
定义格式如下:
- const 数据类型 数据成员名 const int x;
- 数据类型 const 数据成员名 int const x;
-
常数据成员必须初始化,定义对象时通过构造函数的初始化设定初始值,此后不能该表
-
定义格式如下:
- 类名 *const 指针变量名=对象地址 Date *const p=&test1;
-
指向对象的常指针是常量,不得改变指针的指向;但该指针指向的对象的数据成员值是可以改变的
-
定义格式如下:
- const 类名 *指针名; const Date *p;
-
指向常对象的指针是指这个指针所指的对象是不能改变的,但指针是可以改变指向的
-
常对象只能用指向常对象的指针指向它,指向一般对象的指针是不能指向常对象的
-
指向常对象的指针不仅可以指向常对象,也可以指向一般对象,但无论指向谁,都不能通过该指针改变对象的值
-
const离指针近,Date *const p=&test1; 则是指向对象的常指针,只是保证指针指向不能改变,但所指的值可变
-
const离指针远(离对象近),const Date *p; 则是指向常对象的指针,指针指向可以改变,但其所指的值不能通过指针更改
-
定义格式如下:
- const 类名 &引用名; const Date &test1;
-
常引用也只能调用常成员函数
-
常引用作为函数形参时,函数中不能有修改其对应实参中数据成员值的语句
-
常引用作为函数形参时,实参可以是一般对象、常对象、一般引用、常引用
一般用于计数,在定义数据成员前加上static修饰即可, static int count; 特点:
-
定义对象时不会为静态数据成员分配内存空间,它是类中对象共享的数据,为所有类共同拥有
-
静态数据成员具有静态生存期,也因此,必须在类外初始化,初始化时,不需要加关键字static
class Person { public: //类内定义静态成员变量 static int n; }; //类外进行初始化 int Person::m_A = 0;
- 如果静态函数是公有的,在类外可以直接访问;如果是保护的或私有的,在类外只能通过对象调用公有的成员函数访问
- 静态成员函数是类中所有对象共享的成员函数,既可以通过已定义的任何一个对象或对象指针调用它,也可以通过类名直接调用,也因此静态函数也没有默认的this指针
- 它只能对静态数据成员进行操作,不能访问类的非静态数据成员
内联函数目的是为了提高函数的执行效率,用关键字 inline 放在函数定义前,(注意是定义而非声明) 即可将函数指定为内联函数,内联函数是将它在程序中的每个调用点上“内联地”展开,假设将 max 定义为内联函数:
inline int max(int a, int b)
{
return a > b ? a : b;
}
-
调用函数比求解等价表达式要慢得多,在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行
-
有了内联函数,就能像调用一个函数那样方便地重复使用一段代码,而不需要付出执行函数调用的额外开销。很显然,使用内联函数会使最终可执行程序的体积增加。以时间换取空间
-
内联函数和普通函数的区别在于:当编译器处理调用内联函数的语句时,不会将该语句编译成函数调用的指令,而是直接将整个函数体的代码插人调用语句处,就像整个函数体在调用处被重写了一遍一样
-
内联函数中的代码应该只是很简单、执行很快的几条语句。如果一个函数较为复杂,它执行的时间可能上万倍于函数调用的额外开销,那么将其作为内联函数处理的结果是付出让代码体积增加不少的代价,却只使速度提高了万分之一,这显然是不划算的。有时函数看上去很简单,例如只有一个包含一两条语句的循环,但该循环的执行次数可能很多,要消耗大量时间,那么这种情况也不适合将其实现为内联函数
友元让private修饰的变量属性不再绝对的私有.在必要的时候可以使用friend关键字进行修饰访问
1.友元函数:友元函数提高了程序运行的效率,但是破坏了类的封装性,使用时应该权衡利弊
- 全局函数作为友元函数
//友元函数可以直接访问类的私有成员和受保护的成员 //普通函数作为友元函数(以下使用友元函数求两个矩形面积之和) #includeusing namespace std; class Rect { public: Rect(int xx1=0,int yy1=0,int xx2=0,int yy2=0):x1(xx1),y1(yy1),x2(xx2),y2(yy2){} friend int add_area(Rect &r1, Rect &r2); //友元函数声明 private: int x1, y1, x2, y2; //存储矩形顶点(左上,右下)坐标信息 }; int add_area(Rect &r1, Rect &r2) { return (r1.x2 - r1.x1)*(r1.y2 - r1.y1) + (r2.x2 - r2.x1)*(r2.y2 - r2.y1); } int main() { Rect rect1(3, 4, 5, 7), rect2(6, 8, 16, 18); cout << "两矩形面积之和为:" << add_area(rect1, rect2); //106 }
- 成员函数作为友元函数
//成员函数作为友元函数:友元函数不仅可以是类外的普通函数,也可以是其他类的成员函数 //下面示例中,Teacher类的成员函数setscore()被声明为Student类的友元函数,setscore()函数可以访问Student中的私有成员score //修改学生分数 #include2.友元类:#include using namespace std; class Student; //提前声明Student类 class Teacher { public: Teacher(int i,string n):tid(i),tname(n){} void setscore(Student &st, int s); private: int tid; string tname; }; class Student { public: Student(int i, string n, int s):sid(i), sname(n),score(s){} void show() { cout << sid << " " << sname << " " << score << endl; } friend void Teacher::setscore(Student &st, int s); //友元函数声明 private: int sid; string sname; int score; }; void Teacher::setscore(Student &st, int s) { st.score = s; }//友元函数实现 int main() { Teacher te(5001, "Wang"); Student st(1001, "Zhang", 80); st.show(); te.setscore(st, 85); st.show(); }
8.继承: 1.语法:
继承的基本语法:class 子类(派生类) :继承方式 父类(基类)
语法:class 子类 :继承方式 父类1 , 继承方式 父类2…
#include2.菱形继承:using namespace std; // 基类 class Shape { public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } protected: int width; int height; }; // 派生类 class Rectangle: public Shape { public: int getArea() { return (width * height); } }; int main() { Rectangle Rect; Rect.setWidth(5); Rect.setHeight(7); // 输出对象的面积 cout << "Total area: " << Rect.getArea() << endl; return 0; }
菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承者两个派生类
这种继承被称为菱形继承,或者钻石继承
- 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及产生二义性
- 利用虚继承可以解决菱形继承问题 (下文有虚基类详解,请继续阅读或在目录处直接跳转)
多态性是指同一个函数名具有多种不同的功能。c++支持的多态性主要表现在函数重载、运算符重载、虚函数方面。
1.C++多态方式:(1)静态多态(重载)
是在编译的时候,就确定调用函数的类型。
(2)动态多态(虚函数)
在运行的时候,才确定调用的是哪个函数,动态绑定。运行基类指针指向派生类的对象,并调用派生类的函数。
虚函数就是允许被其子类重新定义的成员函数。而子类重新定义父类虚函数的做法,称为"覆盖"(override),或者称为"重写"。
2.虚函数详解://定义一个Point类,再以它为基类派生出一个圆类Circle #includeusing namespace std; class Point { public: Point(double a = 0, double b = 0) { x = a; y = b; } double area() { return 0; } protected: double x, y; }; class Circle :public Point { public: Circle(double a=0,double b=0,double r=0):Point(a,b),radius(r){} double area() { return radius * radius*3.1415926; } private: double radius; }; void test(Point &temp) { cout << temp.area() << endl; } int main() { Point p(1, 1); Circle c(1, 1, 10); cout << p.area() << endl; //0 cout << c.area() << endl; //314.159 test(p); //0 test(c); //0 非预期! //在这里,传递不同类的对象给引用temp,系统分辨不清传递过来的是基类对象还是派生类对象,c++提供的虚函数能够解决上述不能正确分辨对象类型的问题 return 0; }
//使用虚函数(相比上面程序,只是在基类Point的成员函数area前加上了virtual改为虚函数之后,得到了我们预期的结果) #includeusing namespace std; class Point { public: Point(double a = 0, double b = 0) { x = a; y = b; } virtual double area() { return 0; } //将基类Point的成员函数area改为虚函数 protected: double x, y; }; class Circle :public Point { public: Circle(double a=0,double b=0,double r=0):Point(a,b),radius(r){} double area() { return radius * radius*3.1415926; } private: double radius; }; void test(Point &temp) { cout << temp.area() << endl; } int main() { Point p(1, 1); Circle c(1, 1, 10); cout << p.area() << endl; //0 cout << c.area() << endl; //314.159 test(p); //0 test(c); //314.159 //预期 return 0; }
- 虚函数的作用是实现动态联编,也就是在运行阶段动态地选择合适的成员函数。
- 定义了虚函数,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应该与虚函数同名且返回类型、参数个数、参数顺序、参数类型都相同,以实现统一的接口,但函数体可以不同。
- 如果在派生类中没有对虚函数重新定义,则它继承基类的虚函数。
- 只能用virtual声明类的成员函数,而不能将类外的一般函数声明为虚函数。因为虚函数的作用是允许在派生类中对基类的虚函数重新定义。
- 动态联编规定:只能通过指向基类对象的指针或基类对象的引用来调用虚函数
1.重载运算符常用的两种方式:c++系统内部定义的运算符主要用于c++基本类型数据的运算,对于自定义类型数据,如类对象的运算不能直接使用。但通过定义重载运算符可以使类对象的运算和基本类型数据运算一样简单。
- 将运算符重载函数写在类中,作为类的成员函数
- 将运算符重载函数写在类外,在类中声明为友元函数
//复数相关运算的运算符重载 #includeusing namespace std; class Complex { private: double real; double image; public: Complex(){real=0;image=0;} Complex(double r,double i) { real=r; image=i; } void ShowComplex() { cout< 0) cout<<"+"; cout< out< 0) out<<"+"; out< > friend istream& operator >>(istream &in,Complex &c) { cout<<"input r:"< >c.real; cout<<"input i:"< >c.image; return in; } //重载 = Complex operator =(const Complex &c) { if(this == &c) return *this; //防止自赋值导致低效 real=c.real; image=c.image; return *this; } //重载 -= Complex operator -=(const Complex &c2) { real-=c2.real; image-=c2.image; return *this; } //重载 +=(两个复数相加) Complex operator +=(const Complex &c2) { real+=c2.real; image+=c2.image; return *this; } //重载 +=(复数与实数相加) Complex operator +=(double c2) { real+=c2; return *this; } //重载 *= Complex operator *=(const Complex &c2) { real=real*c2.real-image*c2.image; image=image*c2.real+real*c2.image; return *this; } //重载/= Complex operator /=(const Complex &c2) { real=(real*c2.real+image*c2.image)/(c2.real*c2.real+c2.image*c2.image); image=(image*c2.real-real*c2.image)/(c2.real*c2.real+c2.image*c2.image); return *this; } //重载 +(复数相加) friend Complex operator +(const Complex &c1,const Complex &c2) { Complex c(c1); c+=c2; return c; } //重载 +(复数与实数相加) friend Complex operator +(const Complex &c1,double c2) { Complex c(c2,0); c+=c1; return c; } //重载 - friend Complex operator -(const Complex &c1,const Complex &c2) { Complex c(c1); c-=c2; return c; } //重载 * friend Complex operator *(const Complex &c1,const Complex &c2) { Complex c(c1); c*=c2; return c; } //重载 / friend Complex operator /(const Complex &c1,const Complex &c2) { Complex c(c1); c/=c2; return c; } }; int main() { Complex c; Complex c1(1.5,2.5); Complex c2(2.5,3.5); cout<<"c1:t"; c1.ShowComplex(); cout<<"c2:t"; c2.ShowComplex(); c=c1+c2; cout<<"c1+c2:t"; c.ShowComplex(); cout<<"c1+5:t"; c1+=5; c1.ShowComplex(); c=c1-c2; cout<<"c1-c2:t"; c.ShowComplex(); c=c1*c2; cout<<"c1*c2:t"; c.ShowComplex(); c=c1/c2; cout<<"c1/c2:t"; c.ShowComplex(); cin>>c1; cout<<"c:"< 11.模板template: //函数模板,模板声明 template12.文件读写: 1.写文件的步骤://建立一个通用函数,函数类型和参数类型都不指定,而是用虚拟类型代表,在函数调用时,系统自动根据实参类型来取代模板里的虚拟类型 template //注意末尾无分号 T1 add(T1 a,T1 b) { return a + b; } //!!!模板头和函数定义(声明)是一个不可分割的整体,它们可以换行,但中间不能有分号。 //!!!以下调用时会报错,因为函数模板只能进行参数推导,而不能进行返回值推导, //因此返回类型要写确定的某种类型,或者如果是虚拟参数,那么必须是参数表里的某个虚拟参数 //template //T1 add(T2 x1, T3 x2) { // return x1 + x2; //} template T1 add(T1 x1, T2 x2) { return x1 + x2; } int main() { double a = 6.2; int b = 6; cout << add(a,b)<< endl; return 0; }
- 包含头文件:#include<“fstream”>
- 创建流对象:ofstream ofs;
- 打开文件:ofs.open(“文件路径”, 打开方式);
- 写数据:ofs << “写入的数据”;
- 关闭文件:ofs,close();
void test1() { ofstream ofs; ofs.open("test.txt", ios::out); //创建的test.txt文件的路径就是程序文件所在的文件路径 ofs << "姓名:Aena" << endl; ofs << "性别:女" << endl; ofs << "年龄:18" << endl; ofs.close(); }2.读文件的步骤:
- 包含头文件:#include<“fstream”>
- 创建流对象:ifstream ifs;
- 打开文件:ifs.open(“文件路径”, 打开方式);
- 读数据:四种方式读取
- 关闭文件:ifs.close();
#include13.“八股文”系列: 1.面向过程和面向对象的区别:#include #include using namespace std; void test1() { //2.创建流对象 ifstream ifs; //3.打开文件,并且判断是否打开成功 ifs.open("c:\test.txt", ios::in); if (!ifs.is_open()) { cout << "文件打开失败" << endl; return; } //4.读数据: string str; while (getline(ifs, str))//getline(ifs, str)是逐行读取ifs中的文件信息 { cout << str << endl; } //5.关闭文件: ifs.close(); } int main() { test1(); return 0; } 2.c++的三大特性:
面向过程:根据逻辑从上到下写代码
面向对象:将数据与函数绑定到一起,进行封装,加快开发程序,减少重复代码的重写过程
3.C++ 重载和重写的区别:
- 封装性: 类将成员变量和成员函数封装在类的内部,根据需要设置访问权限,通过成员函数管理内部状态。
- **继承:**继承所表达的是类之间相关的关系,这种关系使得对象可以继承另外一类对象的特征和能力。作用:避免公用代码的重复开发,减少代码和数据冗余。
- 多态:多态性可以简单地概括为“一个接口,多种方法”,字面意思为多种形态。程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。比如函数重载、运算符重载、模板、虚函数、覆盖
14.《C++面向对象程序设计谭浩强》阅读笔记: 1.part1(零散知识点&模板&setw&内联&new&delete):
重载:C++利用命名倾轧(name mangling)技术,来改名函数名,区分参数不同的同名函数。命名倾轧是在编译阶段完成的。编译时将参数类型加入以区分不同。
重写:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。
#include#include using namespace std; //c++规定main函数必须声明为int型 //endl是end line的缩写,即结束该行,要换行 //函数模板,模板声明 template //建立一个通用函数,函数类型和参数类型都不指定,而是用虚拟类型代表,在函数调用时,系统自动根据实参类型来取代模板里的虚拟类型 template //注意末尾无分号 T1 add(T1 a,T1 b) { return a + b; } int main() { int a = 6,b=66666; //setw(5)的作用是为其后面的一个输出项预留5列,如果输出项长度不足5列,则数据向右对齐,超过则按实际长度输出,需要包含头文件iomanip cout << "a=" << setw(4) << a << endl; cout << "b=" << setw(4) << b << endl; //c与c++常变量 // #define PI 3.14 #define R a+b 这些都是在预编译时进行字符替换,预编译之后,程序中无PI和R, // 因此实际上程序中的PI和R不是变量,没有类型,不占存储单元。 // 容易出错在忽略了它仅仅是替换,而不是计算后替换,如R*R,实际替换成a+b*a+b,而非(a+b)*(a+b) //建议尽量用const取代c语言中用define的方式,如const float PI=3.14 //函数模板 int x1 = add(2, 4); //此时int取代了T1 double x2 = add(1.0, 9.9); //此时浮点型取代了T1 // int x3 = add(2, 5.6); 报错,编译器不知道该用int取代还是用浮点型取代T1,不过多参数类型可在声明模板时声明多个虚拟类型 cout << "x1=" << x1 << endl; cout << "x2=" << x2 << endl; //cout << "x3=" << x3 << endl; } #includeusing namespace std; //!!!模板头和函数定义(声明)是一个不可分割的整体,它们可以换行,但中间不能有分号。 //vs2017多行注释与取消都要摁两组,注释是ctrl+k ctrl+c ,取消注释是ctrl+k ctrl+u //!!!以下调用时会报错,因为函数模板只能进行参数推导,而不能进行返回值推导, //因此返回类型要写确定的某种类型,或者如果是虚拟参数,那么必须是参数表里的某个虚拟参数。 //template //T1 add(T2 x1, T3 x2) { // return x1 + x2; //} template T1 add(T1 x1, T2 x2) { return x1 + x2; } int main() { double a = 6.2; int b = 6; cout << add(a,b)<< endl; return 0; } //c提供的#define MAX 100 的宏定义建议换成c++里常变量定义,const int max=100 //c提供的#define R a+b 的宏定义建议换成c++里的内联函数,inline int add(int a,int b){return a+b;} //因为宏替换不作运算,可能会出现非预期的结果,比如R*R实际上是 a+b*a+b,但内联函数可避免此类问题 //内联函数的定义,只需在首端添加关键字inline //它可以减少函数调用次数(调用函数的时间往往都比执行时间长),因为它是在编译时直接嵌入主函数中 #include2.part2(全局函数为友元&.h文件.cpp文件)://注意cstring和string.h是c提供的,string才是c++新标准,如果只用cstring头文件,那么不能用<<输出字符串 #include using namespace std; inline int max(int a, int b) { return a > b ? a : b; } int a = 521; struct Student { char name[10]; int age; char sex; }; int main() { cout << max(8, 9) << endl; //当局部变量与全局变量同名时,局部变量将屏蔽全局变量,如果同名时想调用全局变量,那么应该在变量名前加上作用域运算符:: int a = 748; cout << "局部变量a=" << a << "," << "全局变量a=" << ::a << endl; //字符串常量以 结束,但将字符串常量赋值给变量时,只存放字符串本身,不包括 string s1 = "lovelycoder";//可以看作字符数组 cout << s1[0]< "come","on","lovelycoder" };//字符串数组,并初始化 cout << s2[2] << endl; // x86是32位 x64是64位,sizeof计算的是所占内存字节数,在x86下,c++为每个string对象开辟28个字节,x64下,开辟40个字节 cout << sizeof(s1) << "," << sizeof(s2) << endl; //28 28*3 //c++提供new和delete运算符(不是函数,不关注是否引入头文件等)取代c中的malloc和free Student*p = new Student;//使用malloc时要指定确定的开辟空间,而new自动计算, //另外malloc一律返回void*,往往还需要进一步进行强制类型转换,而new自动识别,如果new开辟失败,会返回空指针NULL //new和delete是运算符,而malloc和free是函数,因此前者效率也更高。 p->age = 18; cout << p->age << endl; delete p; } //myclass_test.h #include#include #include using namespace std; class Person { public: Person(string s1="lena",string s2="信息科学与技术",string s3="计算机",string s4="女",int x1=16,long long int x2=2023413729,int x3=2023); void Printinfor()const; friend void Getinfor(Person&p); //全局函数作为友元函数 private: string name, institute, major, sex; int age, year; long long int id; }; //myclass_test.cpp #include"myclass_test.h" Person::Person(string s1, string s2, string s3, string s4, int x1, long long int x2, int x3) { name = s1; institute = s2; major = s3; sex = s4; age = x1; id = x2; year = x3; } void Person::Printinfor()const { cout << "姓名:" << name << setw(10) << "性别:" << sex << endl; cout << "年龄:" << age << setw(10) << "学号:" << id << endl; cout << year << "级-" << institute << "学院-" << major << "专业" << endl; } void Getinfor(Person&p) { cout << "姓名:"; cin >> p.name; cout << endl; cout << "性别:"; cin >> p.sex; cout << endl; cout << "年龄:"; cin >> p.age; cout << endl; cout << "年级:"; cin >> p.year; cout << endl; cout << "学院:"; cin >> p.institute; cout << endl; cout << "专业:"; cin >> p.major; cout << endl; cout << "学号:"; cin >> p.id; cout << endl; }3.part3(友元成员函数):#include4.part4(重载正负号以及流提取运算符):using namespace std; //将普通函数声明为友元函数,Test1 class Test2; //下面Test1类中使用了Test2,因此应该提前声明 class Test1 { public: Test1(int i=-1):id1(i){} void showid(){ cout << "id=" << id1 << endl; } void showid2(Test2&); friend void changeid(Test1&, int); private: int id1; }; void Test1::showid2(Test2& t) { cout << "id=" << t.id2 << endl; } void changeid(Test1&t,int change) { t.id1 = change; } //友元成员函数:将一个类的成员函数声明为另一个类的友元函数 class Test2 { public: Test2(int i = -1) :id2(i) {} friend void Test1::showid2(Test2&); private: int id2; }; int main() { Test1 a(01); a.showid(); changeid(a, 02); a.showid(); Test2 b(10); a.showid2(b); } //一个函数可以被多个类声明为朋友,从而可以引用多个类中的数据成员 //友元类的声明 friend 类名; 将可以访问所有成员 //在实际工作中,一般不把整个类声明为友元类 #include5.part5(虚基类):using namespace std; class Complex { public: Complex() { real = 0; imag = 0; }//默认参数的构造函数 Complex(double r, double i) :real(r), imag(i) {} //重载负号 Complex operator -() { return Complex(-real, -imag); } //重载正号 Complex operator +() { return Complex(real, imag); } //重载流提取运算符<< friend ostream& operator<<(ostream&output, Complex&c); private: double real, imag; }; ostream& operator<<(ostream&output, Complex&c) { output << c.real; if (c.imag > 0) cout << "+"; output << c.imag << "i"; return output; } int main() { Complex c1(3, 4), c2, c3; c2 = -c1; c3 = +c1; cout << c2 << endl; cout << c3 << endl; } //虚基类的作用:如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,那么最终的派生类中将保留该间接共同基类数据成员的多份同名成员, //它们作用是一样的,多份被继承过来,浪费了时间和空间,同时在引用这些同名成员时,还必须加上::以避免二义性 //c++提供虚基类的方法,使得在继承间接共同基类只保留一份成员 #include6.part6(文件操作):using namespace std; //如下设计一个结构,0是1和2的共同基类,然后又让3继承1和2 //在1和2中virtual声明0是虚基类 class Test0 { public: Test0(int i) : id(i) {} void displayid()const { cout << "id=" << id << endl; } protected: int id; }; //虚基类的声明不是在声明基类时,而是在声明派生类时,在指定继承方式前用virtual标识 class Test1:virtual public Test0 { public: Test1(int i,int a) : Test0(i),age(a) {} void displayage()const { cout << "age=" << age << endl; } protected: int age; }; //为了保证虚基类在派生类中同名函数只继承一次,应该在该基类的所有直接派生类中声明为虚基类,否则仍会出现多次拷贝 class Test2:virtual public Test0 { public: Test2(int i,int s) :Test0(i),size(s) {} void displaysize()const { cout << "size=" << size << endl; } protected: int size; }; class Test3 :public Test1,public Test2 { public: //一般情况下的派生类构造函数,只需调用所有直接基类的构造函数, //而在虚基类的使用中,最后派生类的构造函数,不仅需要调用直接基类的构造函数,还要调用虚基类的构造函数 //以下,虽然调用Test1和Test2的构造函数也包括了调用Test0的构造函数,这样岂不是调用了3次Test0的构造? //并不会调用3次,对此,c++编译系统只执行最后的派生类对它的调用,而忽略其它函数对它的调用 Test3(int i,int a,int s,int h) :Test0(i),Test1(i, a), Test2(i, s), height(h) {} void show()const { displayid(); displayage(); displaysize(); cout << "height=" << height << endl; } private: int height; }; int main() { Test3 x(2022, 20, 110, 162); x.show(); } //打开文件的方式: //ios::in为内存输入而打开文件。是指文件为了输入到内存中(显示出来,即这是读) //ios::binary以二进制形式打开文件 //ios::out为输出内存而打开文件。输出内存即是为了将这些数据写入文件中,如果文件已有,那么会覆盖原内容 //ios::app 即追加写入,不会覆盖原内容 //这些方式可以通过或运算符‘|’组合使用 //例如,如果我们想要以二进制方式打开文件"example.bin" 来写入一些数据,我们可以通过以下方式调用成员函数open()来实现: //fstream file; //文件输入(读)类ifstream 文件输出(写)类ofstream 文件读写类fstream //file.open("example.bin", ios::out | ios::app | ios::binary); #include7.part7(异常处理throw&try&catch):using namespace std; //以下>> <<作为流运算符时,是iostream头文件中定义的,但在这里不需要再包含头文件iostream,因为fstream头文件中引入了头文件iostream int main() { fstream examplefile("example.txt"); //生成一个fstream类对象,该对象的初始化即文件名的定义 if (examplefile.is_open()) { examplefile << "This is a line.n"; //将这句话“流入”到examplefile中 examplefile << "This is another line.n"; examplefile.close(); } return 0; } #include15.期末考试之概念题目汇总(已经过筛选):#include using namespace std; //throw抛出异常 //计算圆的面积 double Circle_area(double r) { if (r == 0)throw "半径不能为0!"; return 3.14*r*r; } //计算圆的周长 double Circle_perimeter(double r) { if (r == 0)throw "半径不能为0!"; return 2 * 3.14*r; } //try catch //try{A程序块} catch{Exception e}{B程序块} A程序块有可能会出错,B则是如果A中有了错误,进行的处理 //计算圆锥的表面积 double Cone(double r,double h) { //这里的r为圆锥底圆的半径 try { double s1 = Circle_area(r); double s2 = 0.5*Circle_perimeter(r)*sqrt(h*h + r * r); return s1 + s2; } catch (char* message) { cerr << message << endl; //cerr输出错误,输出throw抛出的信息 return -1; } } //计算圆柱体的表面积 double Cylinder(double r, double h) { try { double s1 = Circle_area(r) * 2; double s2 = Circle_perimeter(r)*h; return s1 + s2; } catch (char* message) { cerr << message << endl; return -1; } } int main() { double cone1 = Cone(3.9, 6); cout << "S:cone1=" << cone1 << endl; double cone2 = Cone(0, 6); cout << "S:cone2=" << cone2 << endl; }
- 在C++中,用于实现运行时多态性的是( D )。
A)内联函数 B)重载函数 C)模板函数 D)虚函数
- 下列关于运算符重载的叙述中,正确的是( B )。
A)通过运算符重载,可以定义新的运算符 B)有的运算符只能作为成员函数重载
C)若重载运算符+,则相应的运算符函数名是+ D)重载二元运算符时,必须声明两个形参
- 有如下类声明,则类MyDERIVED中保护的数据成员和成员函数的个数是( B )。
class MyBASE { private: int k; public: void set(int n){ k=n;} int get( )const{ return k;} }; class MyDERIVED: protected MyBASE { protected: int j; public: void set(int m, int n){ MyBASE::set(m); j=n;} int get( ) const { return MyBASE::get( )+j; } };A)4 B)3 C)2 D)1
- 已知在一个类体中包含如下函数原型: Volume operator-(Volume)const;,下列关于这个函数的叙述中,错误的是( B)。
A)这是运算符-的重载运算符函数 B)这个函数所重载的运算符是一个一元运算符
C)这是一个成员函数 D)这个函数不改变类的任何数据成员的值
一元运算符的重载:一元运算符只对一个操作数进行操作:
常见的一元运算符重载:
- ++ – 自增 自减运算符
- 正负号(+ - )
- 逻辑非运算符( ! )
- 在下列函数原型中,可以作为类AA构造函数的是( D )。
A)void AA(int); B)int AA(); C)AA(int)const; D)AA(int);
- 要实现动态联编,必须通过( A )调用虚函数。
A)对象指针 B)成员名限定 C)对象名 D)派生类名
- 以下( C )成员函数表示纯虚函数。
A)virtual int vf(int); B)void vf(int)=0;
C)virtual void vf()=0; D)virtual void vf(int){}
- 在表达式 x+yz中, + 是作为成员函数重载的运算符,* 是作为非成员函数重载的运算符。则 operator+ 有 个参数,operator 有 参数。( C )
A)2、2 B)2、1 C)1、2 D)1、1
二、填空题(每空2分,共20分。)
17、面向对象程序设计有四个主要特点,即抽象、封装、继承和多态性。
18、非成员函数应声明为类的friend__函数才能访问这个类的private成员。
19、派生类中的成员不能直接访问基类中的__private__成员。
20、在用class定义一个类时,数据成员和成员函数的默认访问权限是private__。
21、运算符重载函数可能是类的 友元函数,也可以是类的_成员_函数,还可以是普通函数。
23、含有纯虚函数的类称为__抽象类__。
一、选择题
2.在下列函数中,是类MyClass的析构函数的是( C )。
A)~Myclass(); B)MyClass();
C)~MyClass(); D)~MyClass(int n);
3.关于this指针的说法错误的是( D )。
A)this指针不能被显示说明 B)对象调用的成员函数中,this指向该对象
C)普通成员函数一般都拥有this指针 D)静态成员函数拥有this指针
4.下面有关new运算符的描述,错误的是( B )。
A)使用new运算符创建对象时,会调用类的构造函数
B)使用new运算符创建数组时,必须定义初始值
C)使用new运算符创建的对象可以使用delete运算符删除
D)new运算符可以用来动态创建对象和对象数组
5.下面关于时间类Time定义的说法中,正确的是( D )。
Time time1,time[30];
Time pTime;
Time &time2=time1;
A)time是一个数组,它具有30个元素
B)pTime就是指向Time类对象的指针
C)time2是一个类对象引用,定义时必须进行初始化,使之成为对象time1的别名
D)以上答案都正确
6.当不同的类具有相同的间接基类时,具有的特点是( D )。
A)各派生类对象中不存在基类版本
B)派生类对象无法产生自己的基类版本
C)为了建立唯一的间接基类版本,应该改变继承方式
D)为了建立唯一的间接基类版本,应该声明虚基类
8.运算符表达式obj1>obj2重载为类的成员函数,被C++编译器解释为( D )。
A)operator>(obj1,obj2) B)>(obj1,obj2)
C)obj2.operator>(obj1) D)obj1.operator>(obj2)
10.在派生类中重新定义虚函数时,除了( D ),其他都必须与基类中相应的虚函数保持一致。
A)参数个数 B)参数类型 C)函数名 D)函数体
- 下列有关重载函数的说法中正确的是( C )
A)重载函数必须具有不同的返回值类型
B)重载函数参数个数必须相同
C)重载函数必须有不同的形参列表
D)重载函数名可以不同- 所谓动态多态性是指 ( B )
A)不同的对象调用不同名称的函数
B)不同的对象调用相同名称的函数
C)一个对象调用不同名称的函数
D)一个对象调用不同名称的对象- 实现两个相同类型数加法的函数模板的声明是( D )
A)template add(T x,T y) B)template T add(x,y)
C)template T add(T x,y) D)template T add(T x,T y)- 以下基类中的成员函数表示纯虚函数的是( A )
A)virtual void tt()=0 B)void tt(int)=0
C)virtual void tt(int) D)virtual void tt(int){}- 在C++中,使用流进行输入输出,其中用于屏幕输入( A )
A)cin B)cerr C)cout D)clog- 下列虚基类的声明中正确的是( D )。
A)class virtual B:public A B)virtual class B:public A
C)class B:public A virtual D)class B: virtual public A- 下列关于虚基类的描述中,( D )是错误的。
A)虚基类的关键字是virtual
B)使用虚基类可以解决公共基类的二义性问题
C)虚基类能够解决公共基类只被初始化一次数据成员的问题
D)带有虚基类的派生类构造函数与不带有虚基类的派生类的构造函数没有区别- 在文件操作中,代表以追加方式打开文件的模式是( B )。
A)iso::ate B)iso::app C)iso::out D)iso::trunc- 下列关于read()函数的描述中,( D )是正确的。
A)是用来从键盘输入中读取字符串的
B)所读取的字符串长度是不受限制的
C)只能用于文件操作中
D)只能按规定读取指定数目的字符二、是非题。描述正确请在圆括号中写T、错误写F。
16.流对象成员函数补充:
1.( T )设p为指向一个动态对象的指针变量,则执行delete p;语句时,将自动调用该类的析构函数。
2.( T )假设有一个Test类,则执行“Test a(5),b[2],*p;”语句时,自动调用该类构造函数的次数为3次。
3.( F )一个类中可以定义多个构造函数和析构函数。
4.( T )类的静态数据成员的初始化在类外进行。
5.( T )若外界函数想直接访问类的私有数据成员,则必须把该函数声明为类的友元函数。
6.( F )如果一个类有两个或两个以上直接基类,则这种继承称为重复继承。
7.( F )在C++语言中,设置虚基类的目的是实现代码复用,通过关键字virtual来标识虚基类。
8.( T )继承具有传递性,即当基类本身也是某一类的派生类时,底层的派生类也会自动继承间接基类的成员。
9.( F )运算符表达式obj1>obj2重载为类的成员函数,被C++编译器解释为operator>(obj1,obj2)
10.( T )包含一个或多个纯虚函数的类称为抽象类。(同13题)
11.( F )假设有一个Test类,则执行“Test a(5),b[2],*p;”语句时,自动调用该类构造函数的次数为4次。(与与选择题10重)
12.( F )C++中,编译时的多态性要通过函数重载来实现,运行时的多态性要通过虚基类实现。
13.( F )包含一个或多个纯虚函数的类称为虚基类。
14.( T )模板可以实现程序设计中的代码重用,体现了面向对象程序设计的可重用性。
15.( F )一个基类可以有多个派生类,一个派生类只能有一个基类。
16.( T )派生类析构函数的执行顺序与构造函数的执行顺序相反。
17.( T )C++中的文件按存储格式可以分为两类,分别是文本文件和二进制文件。
18.( T )C++的流库预定义了cin、cout、clog 和cerr四个流类对象。
19.( T )假如一个类的名称为MyClass,使用这个类的一个对象初始化该类的另一个对象时,可以调用静态成员函数来完成此功能。
20.( F )对赋值运算符进行重载时,应声明为友元函数。
21.( T )函数模板实例化后是模板函数;类模板实例化后是模板类。输入流常用成员函数: 1 read()函数 istream& read(char *pch, int nCount); : 从输入流读取nCount个字符并将其放入pch所指的缓冲区. 如果读取字符数量少于nCount则设置failbit错误位. 2 get()函数 istream&/int get(); 从指定的输入流中输入一个字符(包括空白字符), 并返回该字符作为函数调用的值; 遇到输入流的文件结束时,此get()函数返回EOF. istream&/int get(char& rch); 从指定的输入流中输入一个字符(包括空白字符), 并存储到rch中. 遇到输入流的文件结束时,此 get()函数返回0. 否则返回对istream对象的引用, 并用该引用再次调用get()成员函数. istream&/int get(char& pch, int nCount, char delim='n'); 从指定输入流读取字符. 函数要么读取到nCount个字符后终止, 要么读取到指定字符delim终止. 函数把读取的字符存储到数组中, 并在字符串后加结束符' '. 终止符不会存储在数组中, 但仍保留在输入流中(终止符就是要读取的下一个字符), 所以除非终止符在输入流被删除(可以使用ignore()函数), 否则紧接着的第二个get()操作的结果是终止符, 默认情况下是终止符的默认值'n' 3 getline()函数 读取一行字符串后在数组末尾加' '. 读取后删除结束符. 4 gcount()函数 无参函数, 统计最后一次输入操作读取的字符数. 5 ignore() istream& ignore(int n = 1,int t =EOF); :遇到指定的终止符t时提前跳过输入流中的n个字符结束(此时跳过包括终止字符内的 若干个字符). 终止字符仍然停留在输入流中. 6 putback()函数 istream& putback(char ch); : 把上一次从输入流中通过get()或者getline()取得的字符再放回到该输入流中. 对于应用程序 需要扫描输入流以查找特定字符开头的字段来说, 这是非常有用的. 当输入一个字符时, 应用程序把该字符放回输入流, 以保证 输入的数据包含该字符 7 peek()函数 无参函数, 返回输入流的下一个函数, 但并不从输入流中将其删除. 作用是观察该字符, 字符指针仍停留在原来位置上 输出流常用成员函数 1 write()函数 ostream& write(const char *pch, int nCount); : 把nCount个字符从pch所指的缓冲区写入输出流 2 put()函数 ostream& out(char rch); : 用于输出一个字符 setw(n)函数是C++中关于在输出操作中使用的字段宽度设置函数,其中n表示字段宽度,它的头文件#include< iomanip > setw(int):为设置宽度 setfill(char):为填充的字符串 都在iomanip的头文件中。#include#include using namespace std; int main(){ int data = 0x01020304; cout << hex << setw(8)<< setfill('0') << data << endl; int data_2 = 8; cout << hex << setw(8) << setfill('0') << data_2 << endl; cout << hex << setw(7) << setfill('0') << data_2 << endl; cout << setw(6) << setfill('1') << data_2 << endl; getchar(); return 0; }



