抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

一些题目

多态:函数名相同,但是具体内容不同。

重载:

1
2
int fun(int a,int b);
int fun(double a,int b);

虚函数:

1
2
3
4
5
6
class car:
virtual void print()
class taxi:
void print(){taxi...}
class bus:
void print(){bus...}

拷贝:必须是引用,而且不能改变,所以是 const xxx & xxx

需要 delete p[] 才能调用析构函数,因为是指针(相关可以参考为什么会内存泄露)。

private 继承不可访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
void myfun(int *q, int&n){
cout<<*q<<endl;
q=&n;
cout<<*q<<endl;
}
int main(){
int x=0, y=1, z=2;
int *p=&x;
myfun(p, y);
cout<<*p<<endl;
myfun(&z, *p);
return 0;
}

传入 int *q 不会改变原来 p 指向的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
using namespace std;
class Dummy
{
int objectID;
public:
Dummy(int ID)
{
objectID=ID;
cout <<"Constructor"<<objectID<< endl;
}
Dummy(const Dummy &source)
{
objectID=source.objectID;
cout<<"Copy constructor"<<objectID<<endl;
}
~Dummy()
{
cout<<"Destructor"<<objectID<< endl;
}
};
Dummy obj0(0);
void f1(Dummy aDummy)//传入需要复制
{ static Dummy obj1(aDummy); }
void f2(Dummy& aDummy)
{ Dummy obj1(aDummy); }
int main()
{
Dummy obj2(2), obj3(3);
f1(obj2);
f2(obj3);
obj2 = obj3;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Constructor 0
Constructor 2
Constructor 3

Copy constructor 2
Copy constructor 2
Destructor 2
(f1 函数,需要copy两次,最后传进来的参数进行析构,但是static本身先不析构,需要main函数结束,为什么是这样的呢,因为如果函数调用结束就析构,就不符合static的定义)

Copy constructor 3
Destructor 3
(引用传参不用copy)

Destructor 3
Destructor 3
(main函数中的两个变量进行析构,赋值不会调用复制)

Destructor 2
(static析构)
Destructor 0
(全局变量析构)

加上

1
2
3
4
5
6

Dummy& operator = (const Dummy &source){
objectID=source.objectID;
cout<<"= operator"<<objectID<<endl;
return *this;
}

则会出现

1
2
3
4
Destructor3
--> = operator3
Destructor3
Destructor3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;
class A
{ private:
int x;
public:
A(int a=8){x=a;}
A(const A&r){x=r.x*5;}
A& operator=(const A &r)
{ x=2*r.x;
return *this;
}
void disp(){cout<<"x:"<<x<<endl;}
~A() { cout<<x<<" bye "<<endl;}
};
A a0=7;
int main()
{
A a1(3);
A a2=a1;//A .... 调用复制
a2=a1;//a2=a1 调用=运算符
static A a3;
a2.disp(); a0.disp();
return 0;
}

在指针定义里,const的作用取决于const和 * 的相对位置。如果const在 * 右边,则代表指针p本身不能改变,即 p 不能指向其他地方;如果 const 在 * 左边,则代表指针p指向的区域不能改变,但是p可以指向其他地方

const int * const p

&a 类型是 int**,值还是 a 的值;因此 &a + 2 是对a这个地址加上2 * sizeof(int* ),这里的sizeof(int*)是指针的大小,指针的大小取决于系统是32位还是64位的,所以 *(&a + 2) 在不同的机器上值可能不同

image-20230105110909874

类型

1
2
3
4
5
6
7
cout<<sizeof(int)<<endl; 
cout<<sizeof(long)<<endl;
cout<<sizeof(long long)<<endl;
cout<<sizeof(char)<<endl;
cout<<sizeof(bool)<<endl;
cout<<sizeof(double)<<endl;
cout<<sizeof(float)<<endl;
1
2
3
4
5
6
7
4
4
8
1
1
8
4

一个 byte 是 44 个 bit。

原码:正数符号位为 00,负数符号位为 11

反码:正数的反码和原码相同,负数的反码为原数的绝对值的原码取反。

补码:正数的补码和原码相同,负数的补码为反码+1.

优先级: ! >算术 > 关系 > && > || > 赋值

其中,关系运算符 >=,>>=,> 又先于 ==,!===,!=

短路求值:逻辑表达式在执行时,先处理左边。如左边已能决定此逻辑表达式的结果,则右边不执行。通过巧妙设计,可以减少程序执行时间。

1. 执行int a = 1, b = 2, c = 2, d = 4; bool m=1, n=1; bool result=(m = a > b) && (n = c > d);

则m,n,result的值分别为 A

A、0, 1, 0  B、0, 0, 0 C、1, 2, 0 D、1, 0, 0

字符串

image-20230105143535597

cin.get() 相当于 getchar,但是可以两种写法。

强制类型转换

1
2
(类型名)(表达式)
类型名(表达式)

算术运算符左结合,赋值运算符右结合。

for循环等价:

1
2
3
4
5
6
7
for(A;B;C)
A;
while(true){
if B
...
C
}

cin.getline(字符数组,数组长度,结束标记)

cin.get将结束字符留在输入流中,而cin.getline将结束字符从输入流中删除

不包含结束标记。

只有在输入完数据再按回车键后,该行数据才被送入键盘缓存区,形成输入流,该回车字符也被送到缓存区

>> 忽略所有开始时的空白字符,遇到空白字符结束,空白字符仍保留在输入流中。
>> 向字符串数组输入时不检查输入个数是否超过数组长度,易产生内存溢出问题;向字符变量输入时,则存入单个字符就结束
cin.get,cin.getline:不忽略空白字符,遇到指定结束字符结束,或者达到指定长度结束
cin.get结束时不删除流中的结束字符,cin.getline结束时删除流中的结束字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

int main()
{
char ch1[80],ch2[80],ch3[80],ch4[80];
cin>>ch1;
cin.get(ch2,80,'.');
cin.getline(ch3,80,',');
cin.getline(ch4,80,';');
cout<<ch1<<endl<<ch2<<endl<<ch3<<endl<<ch4<<endl;
return 0;
}

输出

1
2
3
4
aaa
bbb
.ccc
ddd
1
2
3
4
5
6
7
strcpy(dst,src);
strncpy(dst,src,n);
strcat(dst,src);
strncat(dst,src,n);
strchr(s,ch);//第一次出现
strrchr(s,ch);//最后出现
strstr(s1,s2);//第一次出现 s2 的地址

函数

1
2
返回类型 函数的名字(形式参数说明);
函数名(实际参数表),值传递、引用传递。
1
2
3
4
template<class T>
T max(T a,T b){
return ...
}

函数重载形参不一样也可能会产生错误,如:

1
2
int f(int x,int y=10);
int f(int x);
1
2
3
4
5
6
7
8
void  Hanoi(int n,  char start, char finish, char temp)
{ if (n==1) cout << start << "->" << finish << '\t';
else { Hanoi(n-1, start, temp, finish);
cout << start << "->" << finish << '\t';
Hanoi(n-1, temp, finish, start);
}
}

判断回文数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
using namespace std;
void huiwen(int n);

int main()
{
int n;

cout<<"n=";
cin>>n;

huiwen(n);
return 0;
}
void huiwen(int n){
if (n<10){
cout<<"Yes"<<endl;
return ;
}
int bg=n,ps=1;
while (bg>=10) bg/=10,ps*=10;
int ed=n%10;
if (bg!=ed){
cout<<"No"<<endl;
return ;
}
huiwen((n-bg*ps-ed)/10);
}

image-20230105102441911

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main()
{int lh, rh, mid, x;
int array[ ]={0,1,2,3,4,5,6,7,8,9};
cout <<"请输入要查找的数据:"; cin >> x;
lh = 0; rh = 9;
while ( lh <= rh )
{ mid = ( lh + rh ) / 2;
if ( x== array[mid] ) {
cout << x << "的位置是:" << mid << endl;
break; }
if ( x < array[mid]) rh = mid - 1; else lh = mid + 1;
}
if (lh > rh) cout << "没有找到" << endl;
return 0;
}

二分查找

1
2
3
4
5
6
int lh, rh, array;
输入要排序的元素,存入array;
for (lh = 0; lh < n; lh++)
{在array的从lh到n – 1的元素之间找出最小的元素的下标放入rh;
交换下标 lh和 rh中的值;}
输出排好序的元素;

选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main()
{int a[ ] = { 0, 3, 5, 1, 8, 7, 9, 4, 2, 10, 6};
int i, j, tmp, n = 11;
bool flag;
for (i=1; i<n; ++i)
{
flag = false;
for (j=0; j<n-i; ++j)
if (a[j+1] < a[j])
{tmp = a[j]; a[j] = a[j+1]; a[j+1] = tmp;
flag = true;}
if (!flag) break;/* 一趟冒泡中没有发生交换,排序结束*/
}
cout << endl;
for (i=0; i<n; ++i) cout << a[i] << ' ';
return 0;
}

冒泡排序

指针

引用必须定义时初始化,而且定义之后不能作为另一个变量的别名,和指针的主要区别在这里。

多级指针

1
2
3
4
char *string[10];
int x;
int *p=&x;
int **q=&p;

动态二维数组。

*(a+x) 等价于 a[x]

lambda表达式:

1
2
auto f=[](int x,int y){return x*y};
f(x,y);

分配空间比较好的写法:

1
2
3
4
storage=new int [....]
if (storage==NULL){
处理分配不了空间的情况。
}

为了避免二次回收空间可以:

1
if (storage) delete[] storage;

image-20230105102631764

声明数组的时候代表的是数组某一维的大小。

面向对象

类的创建者,类的使用者。(实现隐藏)

类的继承或派生:

  • 原有:基类
  • 新建:派生类

结构体

结构体可以是左值,但是数组不能是左值。

用结构体类型名::函数名进行区分。

1
2
3
4
5
6
class 类名{
private:
私有数据成员和成员函数。
public:
公有数据成员和成员函数。
}

传参最好 const 类名 &x,因为不会改变原来的值。

1
2
3
4
5
6
7
8
Rational *rp;
rp=new Rational;
rp->display();
rp=new Rational[20];
(rp+1)->display();
rp[1].display();
delete rp;
delete [] rp;

构造函数

1
2
3
DoubleArray::DoubleArray(int lh,int rh):low(lh),high(rh){
storage=...
}

复制构造函数

1
2
3
4
DoubleArray::DoubleArray(const DoubleArray&c){
//需要开辟一块相同大小的内存空间
//编译器内置的复制构造函数只是把数组首地址复制了过来,没有开辟新空间,会造成很多问题
}
1
2
Rational r1(1,2),r2(r1),r3=r1;//后面两个都是复制构造函数
r3=r2;//等于运算符,不考

image-20230109222802918

析构函数

1
2
3
DoubleArray::~DoubleArray(){

}

构造和析构的过程和栈有不同,因为有静态变量的存在。

一般对象传递都采用引用传递,因为对象中包含很多数据成员,占用空间较多。

image-20230108104421240

常量数据成员、常量对象、常量成员函数

1
2
3
4
5
6
7
class 类名{
private:
const int size;
public:
类名(int sz):size(sz){}
void disp() const{}//是不修改数据成员的
}

常量对象只能初始化,必须初始化,不能赋值。

静态数据成员、静态对象、静态成员函数

静态数据成员是类一级的共享。

必须初始化

1
double 类名::rate=0.05;

静态成员函数用于操作静态数据成员,是为整个类服务的,如:

1
2
3
static void change(double newRate){
rate=newRate;
}
1
2
3
类名::change(...);
对象.change();//也可以
对象指针->change();

静态成员函数。没有 *this,只能访问静态数据成员。

image-20230109224642072

静态常量成员

1
2
3
class xxx{
static const int SIZE=10;
}

友元

1
2
friend 函数抄一遍
friend class xxx

image-20230105111331193

组合和继承

组合比较简单。

继承(派生)

1
2
3
class Derived:public(派生方式,保持基类的访问特性) Base{
....
}

protected:可以被派生类的成员函数和友元函数访问,但是不能被全局函数和其他类的成员函数访问。

构造函数
1
2
3
Derived(xxx):Base(xxx){

}

构造顺序:基类->对象成员->自己的构造函数。

析构顺序:相反

运算符重载

  1. 如果要支持Rational到double的类型转换,需要在Rational类中声明并定义 成员函数。

    **operator double()**

不能重载:.,.*,::,?:,sizeof

一些总结

构造、析构、复制在什么时候调用

全局变量先构造,然后进入程序块,遇到新定义的变量,和函数传进来的实际参数(引用不会构造),如 A a, A* b=new A[10],int f(A c),这个时候构造。遇到静态变量的时候才执行构造,但是静态变量的析构要等到调用静态变量的程序块整个结束才会析构。程序块的变量结束后自动析构,但是指针不会自动析构,需要 delete []b

复制出现的情况:A c(d),函数传入实际参数,函数返回参数。注意等于运算符和复制的区别。

image-20230109223314768

评论