C++程序设计基础

时间:2023-11-12 15:02:56

01

1 预编译常用的有,宏定义和包含库。
2 库:是实用工具的集和,由程序员编写,可以完成一些特定的功能。
3 <> 系统库 ""用户自定义库。
4 宏定义:定义符号常量,符号常量就是给常量取的名字。常量是在程序运行中不变的值。
  #define Pi 3.1415 (编译后,进行用3.14替换PI,写用PI) 这里的PI 就是给常量3.14取的一个名字。预编译后进行替换。
  此处是C语言常用。
  C++中一般使用const int pi=3.1415;(必须赋初值,以后不能改)
5 名字空间:大型程序由很多源文件组成。有可能很多人编写的,而每个人都可以定义自己的函数,变量等。
  为了避免重名,引入名字空间。同一个名字空间不可重名。
6 C++标准库中的实体都是定义在名字空间std中。所以在引用标准库的中名字,都需要指出名字空间std中的实体。
  这解决了一疑问,就是using namespace std 和没有 它的情况,前者 使用了后,就不要在指明。
7 C++处理的每一个数据必须指明类型。一个数据类型就是事先定义好的工具。2个特征:在内存如何表示,允许执行哪些运算。
8 整型数的溢出:设想 0111111111111111 (+32767) 让他进行+1 操作,就会发生不是按照正常的想法来的+32768,而是
  100000000000000(注意,此处不是-0,由于负数是用补码表示,因此,这个是-32768)
9 实型的分类:float(4B),double,long double(8B,这个都是8B 为什么还要分long和非long呢?)
  在float中,1B用于存指数,3B用于存尾数,因此,float表示的范围是:2的-128次方 到 2的127次方。
  注意:实数在计算机内是不能准确表示的,因此,无法对两个实数采用相等比较。
10 实数常量:第一种,123.4,第二种,科学计数:尾巴*10的指数次方 123*10的三次方,可以记着:123e(E)3
   注意:e后必须是整数,e前必须由数字,如10的5次方,要表示成1e5
11 比较字符是比较他们的ASCII值,字符也就是‘2’,‘A’,如何判断字符为数字,大写字母?
   if(ch>='0' && ch<='9')
   if (ch>='A' && ch<='a')
12 如何输出“” ,需要使用转义字符 \, 另外一个,转义字符\177(其中177是八进制,对应十进制为127),‘\n’ 也是字符常量。
13 typedef 就是重新命名类型名 如 typedef int INT,这个时候,就可以用INT,和int 作用一样功能。
14 在执行运算过程中,c++ 只执行同类型的数据进行运算。因此 会发生自动类型转换。转换原则:非标准 转换成标准类型,占用
   空间少的像大的进行转换。数值范围小的向范围大的靠拢。
15 如果输入的变量是字符,可以通过,cin.get(ch) or ch=cin.get(); (ch='a')
   注意:>> 认为空格和换行有特别的功能,会把它当作当成空白符号,而get 函数可以接收任何字符,包含 空白。

16 关系运算也就是大小于,等于等,>,<,==,!=,的优先级高于前两个高于后面的两个

17 !的优先级最高) &&(第二)  ||(最低),如 !p || q && r (先计算!p 然后q&&r)

18 如何判断一个浮点数是否为0?if(fabs(a)<1e-6)   cout<<“it is zero”

02

1 如何求x的i次方 ?

对于,for(int i=1;i<n;i++) x*=x;
    这个应该是错误的,因为每次的结果,x的值发生了变化
    就得不到i个x乘积的结果
    正确的应该是:
    for(int xn=1,j=1;j<=n;++j) xn*=x;
    此为:x的i次方的结果
   其实,可以用cmath库的一个函数 pow(x,i)

2 求n的阶乘的两个方法
 a:递归
 b:利用for循环,
 for(int j=1;j<=n;j++)
     int result=1;
     result*=j;

3 求解e的x次方代码:

#include<iostream>
using namespace std;
double ex(int x){
double ex=;
double p=;
int i=;
while(p>1e-){
ex+=p;
++i;
p=p*x/i;
}
return ex;
} int main(){ int x;
cout<<"input x:"<<endl;
cin>>x;
double e=ex(x); cout<<"e 的"<<x<<"次方等于:"<<e<<endl;
return ;
}
1 内存溢出:访问数组的时候,由于不会检查下标,所以,访问一个int a[10] ,使用a[11],导致出现内存溢出。
2 在二分查找 需要注意,mid=(lh+rh)/2 因为在while循环中,每次都要作,所以放在whiel内部。
3 判断一段话 由多少个单词
#include<iostream>
using namespace std;
int main(){
char sentence[];
char prev=' ';
int i,counter=; //counter没有赋初值
cin.getline(sentence,);
for(i=;sentence[i]!='\0';++i)//循环写成作80次,完全美必要
{
if(prev==' ' && sentence[i]!=' ') counter++; //==错误写成=
prev = sentence[i];
}
cout<<"have "<<counter;
return ;
}

03

1 函数命名规则:  
  如果1个,用一个小写开头的
  如果有2个,第一个字母大写:UpdateBig()
2 void类型的函数,看作是过程。
3 retrun 表示函数结束。没有返回值,就表示函数结束。
4 函数调用过程:
5 计算return 后面表达式子的值,然后创建一个临时变量,存放return的值。
6 系统为调用函数分配一快内存空间,称为帧。
7 我们知道向函数传递的参数,可以2个,3个,这分为值传递,和引用传递,这个简单
  如果,要向函数传递100个呢,需要传递数组。
  函数原型 应该类似
  int average(int a[10]) //这个10应该没有意义 ,可以看作是引用传递哦,会改变数组的值的。,
  正确的是,(int a[],int n) 前面只是一个地址,
8 内联函数:函数调用会有额外开销(固有的),函数执行也会浪费时间。如果函数小,执行时间短,
  调用内联函数时候,会把内联函数的代码复制插入到调用它的位置上。加长了生成的目标代码。一般都是短小,精悍的代码。
9 重载的必要条件:
  参数数目不同
  参数类型不同
  满足之一就可。
10 静态全局 ,不能被其它原文见引用. 静态局部,其它函数不能引用, 二者特点,在程序结束前都会一直存在。
  在程序结束时,先杀静态局部,在杀全局(不是静态的)
11 extern :外部变量,首先一定时全局变量,

04

1 c++中只运行类型相同的指针互相赋值。
2 void类型指针只说明它是一个地址,但是没有说明存放何种类型,因此,可以与任何类型指针进行赋值。
3 const int* p=&x ,指向常量的指针,*p不能改变,但是p=&y可以
  int * const p=&x,常指针,p=&y不可以,但*p可以改变。由于x地址固定,也就是说它只能指向x的地址。(常量性质)
  综合,指向常量的常指针,只能指向x,且不能修改x的值。
4 与数组的关系:关系非常密切,几乎可以互换使用。
  首先数组名可以看作一个指针,且时常量指针,也就是说,永远指向这一个地址。如果,指针p=数组名,那么,它们可以等价
  就有,p[3]=a[3]
  指针的运算一般之和数组有关,对一个变量的指针运算没有丝毫的意义。p+i = & a[i]
  p1+p2,没有任何意义,但是p2-p1,表示他们之间元素个数。
  数组名可以当作指针,=&a[0]
  但是,他们不完全等价。知道就好。
5 动态内存
  int *p=new int;
  *p=10
  等价与:int *p=new int(10);

int *p=new int[10];
 和普通数组的区别:下标可以时2*n,既可以时表达式的值或者变量,而普通数组,必须时常量。
6 动态变量的存活:即使在函数里面,它也会一直存在,需要使用delete显示删除。
  delete p;
  delete [] p;
  注意,如果时字符数组,可以使用delete p;
7 内存泄露:动态变量不用的时候i,没有delete,就是动态变量没有被释放。导致,内存耗尽。
8 使用断言确定new 是否成功
  #include<cassert>
  #i...
  {
    int *p=new int;
    assert(p!=0); //断言不是真,就退出。
    *p=20;
   }
9 指针的应用:求方程的两个根,使用void SolveQuadratic(double a,double b,double c,double *x1,double x2) ,首先在main函数里面定义两个整形变量,然后传地址到这个函数,这个根的值就可以保存起来了,可以保存两个哈哈。不需要返回值。当然,可以返回其它的值。用作判断。后面两个称为输出参数。
10 数组作为函数参数,本质时地址传递,
  int a[]={2,3,4};
  sizeof(a)=12
  void f(int arr[])
   sizeof(arr) =4 //此处为1个指针占用的字节。
11 统计字符串单词个数
  int WordCount(const char *str) //传递字符串到函数。 它内涵\0
  {
    int cnt=0;
   while(*str!='\0'){
    while(*s==' ') s++; //跳空白
        if(*s!='\0'){
        ++cnt;  //找到一个单词
        while(*s!=' '&& *s!='\0') s++; //跳过单词
     }
   }
  return cnt;
12 函数的返回值可以时一个指针
  比如,从一个字符串中取出一个字符串。char *subString(char *s,int start,int end)
   int len=strlen(s);
   if(start,end yu len de guanxi)
   char *sub=new char[end-start+2];
   strcpy(sub,s+start,end-start+1);
   sub[end-start+1]='\0';
  return sub; //sub时动态的空间,离开函数依然可以用哦。
13 函数的返回值时引用 ,特点时不需要创建临时变量保存返回值,直接返回变量本身,
   要确保这个变量在执行后存在。
14 main函数的参数,int main(int argc,char *argv[]),argc ,所有打出来的参数个数,argv[i],参数字符名字 argv[0]一般表示,执行文件名字

05

1 结构体的定义是如何定义的
struct Student{
    char name[20];
    int age;
}stu1;
stu1,是变量(对象),而Student就相当于一个类型,类似类中的类型名
也可以使用,Student stu2;定义一个结构体变量。
对于
struct{
    char name[2];
    int age;
}stu1;这个就不能在定义变量了,而前者是可以的。
使用花括号进行赋值。
<对于数组,不能使用a={1,2,3},而结构体可以,因此数组不能是左值,结构体则可以>
2 结构体和指针的关系?
结构体可以通过指针来进行访问,其实是和变量一样,都是通过指针简介访问
要注意指针的类型必须是结构体类型名
比如:Student *pst;
类似:
strut Student{
    char name[2];
    int age;
}*pst;
则,可以有pst=&stu1;
或者:pst=new Studnet;
而指针访问通过-> ,如pst->name; 如同stu1.name;
3 什么是结构体数组?
定义了一个类型后,Student,他就可以定义数组了,Student stuArray[10];
4 结构体一般是什么传递?
首先它同基本数据类型一致,都是值传递。(会涉及到复制)
由于结构体一般比较大,选择引用传递(指针过于复杂)
因为指针或引用传递这,形参和实参就公用了一块内存,对形参改动,也就会改动实际参数,要解决这个防止实参被改变,可以使用const限定符,
<关于 A f(); 返回值的是一个副本,其实,也就是临时变量>
struct Point{
 double x,y;
};
int main(){
  Point p1,p2;
  p1=setPoint(1,2);
  cout<<p1;
}
Point setPoint(double a,double b){
    Point p;
     p.x=a;    //在函数结束,p死亡之前,赋值给一个临时变量,所以没问题,对引用是绝对不可以哦,~~,因为,消失了还怎么引用。
    p.y=b;
   return p; //我们在引用返回的时候,是不可以返回局部变量的,但是,这个不是,可以返回,
         //它会创建一个临时变量,在函数结束的时候,回收帧后,然后把零食变量赋给p1;
}
5 链表是作用是什么?链表和数组的关系,可以用数组取代吗?
它用来存储一组同类数据。类似数组,但是数组使用的时候,必须确定元素个数。如果,元素个数不确定,并且数目很大的情况下,使用链表。
它可以进行动态内存分配的结构。不需要实现准备好一块内存空间,当增加一个元素的时候,动态为它申请存储空间,这杨的结果是,元素不是连续存储的。这杨
找元素就比较难了,通过链来指向下一个节点的位置。也就是指向下一个节点的指针。
6 单链表是如何存储的?它是一个结构体?
存储一个单链表值需要存储第一个节点的地址(也就是头指针)
节点类型定义如下(是一个结构体)
struct LinkNode{
    datatype data;
    LinkNode *next;
};
7 单链表常用的有哪些基本操作?
创建,插入,删除,访问;
插入:在p节点后进行插入,步骤:
a:申请一个结点
b:将x放入这个结点数据里面
c:新结点和下一个结点链接
d:和新结点链接(都是针对当前结点(插入其后方))
注意cd顺序,如果不当,可能造成后续结点失踪的问题。
删除:删除结点x
a:找到x结点前的那个结点p
b:然后绕过x就可以了
p->next=p-next->next
注意:p->next 是和LinkNode 等价。
注意:上面的会导致内存泄露。需要回收空间。
完整的:
detPtr=p-next;
p->next=detPtr->next;
delete delPtr;
注意,这种情况假设了要删除的结点前面是有一个结点的情况,特殊的,如何删除第一个结点?同时,也不允许插入在第一个结点前面?
引入头结点来解决这个问题。
8 什么是头结点?作用是什么?
  它不存放任何数据,它的指针指向第一个结点,引入头结点后,依然有头指针head。
  那么有了它,就可以删除第一个结点(有数据的点),以及插入为第一个结点。
  反正,这个家伙很有用,可以简化代码。

#include<iostream>
//值用一个p删除测试
using namespace std;
struct linkNode{
int data;
linkNode *next; //因为这个指针要指向linkNode的数据,就如int 类型的指针指向int 类型数据
};
int main(){
linkNode *head,*p,*q; linkNode a={,NULL}; //可以通过这种结构赋值,因为它是一个结构体 cout<<sizeof(a); return ;
}
#include<iostream>
using namespace std;
struct linkNode{
int data;
linkNode *next; //指向下一个结点的指针,必须有
};
int main(){
int x;
linkNode *head,*p,*rear; //head 头指针,p被操作的当前指针,rear,表尾结点(也就是指向)
head=rear=new linkNode; //此处生成头结点,头指针
while(true){
cin>>x;
if(x==) break;
p=new linkNode;
p->data=x;
rear->next=p;
rear=p;
}
rear->next=NULL; //表尾结点为空。
cout<<"链表内容:"<<endl;
p=head->next;
while(p!=NULL){
cout<<p->data<<'\t';
p=p->next;
}
cout<<endl;
return ;
}
#include<iostream> //约瑟夫环的问题
using namespace std;
struct linkNode{
int data;
linkNode *next;
};
int main(){
int n;
cout<<"please input the number of the people:"<<endl;
cin>>n;
linkNode *head,*p,*q;
head=new linkNode;
head->data=;
p=head;
for(int i=;i<n;i++)
{
q=new linkNode;
q->data=i;
p->next=q;
p=q;
}
p->next=head;
//删除过程
q=head;
/*
while(q->next!=q){ //只要还多于两个结点。
p=q->next;q=p->next; //删除一般需要一个辅助结点
p->next=q->next;
cout<<"被删除的编号是:"<<q->data<<endl;
delete q;
q=p->next; //重新开始 }**/
for(;q!=q->next;){
p=q->next;
q=p->next;
p->next=q->next;
cout<<"被删除的结点:"<<q->data<<endl;
delete q;
q=p->next;
}
cout<<"最后的胜利者是:"<<q->data<<endl; return ;
}

06

1 什么是抽象?怎样理解它?
抽象是解决复杂问题的基本方法。所有的程序设计语言都提供抽象
a:机器语言对硬件进行抽象,程序员通过一些列二进制对计算机操作,而无需理解计算机硬件
b:高级语言对机器语言进行抽象,不用关心下层次怎么操作的。
2 面向过程和面向对象解决问题的思考方式有什么区别,举个例子。
在面向对象设计中,主要工作是根据要解决的位难题创建新的数据类型,用这个类型的对象完成指定工作。
在求园的面积时候,
面向过程的方式:
a:输入半径
b:使用面积公式求出
c:计算出结果
面向对象的方式:
a:首先考虑需要什么工具,需要一个圆,需要保存一个圆
b:这个圆有什么特性,如半径,周长
c:只要定义一个圆类型变量,来保存一个圆。然后这个变量告诉我们面积多少。
3 库和类之间的关系是什么样的?
c++数组的3个问题:
a:下标必须从0开始
b:不检查下标是否越界
c:数组大小必须编译时确定
对于array库的几个缺点说明:
a:每次调用与数组相关的,都要传递一个结构体
b:命名冲突,比如,其他库,也有可能叫initialize的函数,如果调用(注意此时还没有引入类)同时出现,就冲突了
c:用完还需要用户显示的归还空间,不合适。
需要改进:arrayImprove.h
改进后,就避免了命名冲突,此时,函数的调用需要和结构体的变量一样,通过.来进行,而且这些函数隶属于结构体变量的成员
对于.h文件,它的名字的重要作用是要被包含在cpp文件里面。所以,文件名字很重要。要起的好。
4,类和其内联函数什么关系
如果把成员函数的定义直接写在类中,那么就默认是内联函数。
当然,也可以把内联定义写在外面,然后用inline修饰就可以了。
5 如果用指针指向一个动态分配的空间,那么就可以通过指针来访问任意元素,记住,它支持下标访问。
6 对于课本中Raitional类的定义说明
a:工具函数不需要用户自己调用,是类的内部工作。把它定义为私有的,可以通过类的成员函数来访问。
实现方法:找出分子(num),分母(den)最大公因子,让他们分别除以
    int temp=num>den?num:den;
    for(;temp>1;--temp)
        if(num%temp==0 && den% temp ==0){ num/=temp;den/=temp;break;}
    }
7 对象定义有几种方法,区别是什么?
a:普通变量一样定义 Rational r1;
b:动态内存申请 Rational * p=new Rational; 这个时候,释放对象,需要通过delete删除指针就可以了。
     此时,就可以用 p->来访问成员函数。
8 struct 中也有和类一样的隐藏this指针,看来struct和类确实区别不大。
   当用对象调用成员函数的时候,编译器会把相应的对象地址传给形式参数this。

构造函数:不是由类的用户调用,而是由系统在定义对象的时候自动调用,主要工作是对对象进行初始化。
  初始化列表的作用:提高构造函数效率,能够让数据成员初始化和赋值同时进行。
  特殊情况,必须使用:数据成员不是普通类型,是一个类对象,另一个包含常量的数据成员。
9 拷贝构造函数为什么存在?以及什么时候会被调用?
  它以一个同类对象的引用作为参数
  类名(const <类名> &)
  默认系统会自动生成一个拷贝构造函数,通常情况下,默认的就能够完成任务,但是,当数据成员含有动态分配的对象的时候,需要小心,最好自己写一个
a:对象定义时: 如果之前,都定义好的,那么在=还会调用吗,不会,
A a1(a2);
A a1=a2;
b:函数调用时
void f(A a)//要求要是值传递,如果参数时A a,A &b ,那么前者才会复制构造,而后者不会。
A b;
f(b) ; //首先创建一个形参对象a,然后,调用拷贝复制,用b初始化a,
c:函数返回
A f() //用户自定义的类
{  //如果返回的时自定义类的引用会调用马?黑,测试
  A a;
  return a; //执行到return,创建一个临时对象,调用拷贝复制,给临时对象。
}
代码测试:

9 常量对象的存在以及用法?
如果把一个对象定义为常量
const Ration r(2,3);
只能初始化,不能赋值(初始化,会涉及到赋值,但是不一定要=,而=时赋值)
我们知道,修改对象都是通过成员函数来修改,所以,常量对象只允许调用常量成员函数。
const成员函数意思是:它告诉编译器,它时安全的不会修改对象的数据成员的值。
使用的时候,类内声明要加const,类外定义也要加入const,否则将会看成两个函数/
任何不修改对象的成员函数都应该声明为const的。也可以被其它对象调用。
10 常量数据成员如何使用?
它表示,他在对象生存期内是常量,即在对象生成时给出常量值,而在此对象生存期内,它的值不能改变。
class A{
    private :
        const int size;
}
//必须在构造函数中进行初始化,而且一定时在初始化列表中完成。

11 静态数据成员和静态函数存在的意义,以及使用注意?
静态数据成员属于整个类,被它所有的对象共享(意味所有对象都可以使用)。和全局变量的使用区别:缺乏对数据的保护,所有的都能访问,不安全,又体现不出和类的关系
我们知道,类定义只是给出了对象的构成,真正分配空间时在对象生成的时候。由于静态数据不属于对象,所以,静态数据时单独分配的。
只能分配一次,并且在cpp文件中实现。
类内使用static,类外不需要。
比如,我的利率时静态数据,但是,有一天我突然变了,就需要一个静态员函数来修改。这是它存在的意义,类内使用static,类外不需要
它可以通过对象来调用,常见的使用类名调用。
它是为类服务的。最大的特点是没有隐藏的this指针。因此,它不能访问一般的数据成员,只能访问静态数据和其它静态成员函数。
用法:初始化静态数据的值,普通成员函数完成不了的任务。
12 静态常量static const int size的用法
当类需要共享一个常量,使用它。
一般来说,类的数据成员不能在类定义的时候初始化,普通的数据成员是在构造函数时进行初始化。静态数据时在定义时初始化。而这个,必须在类定义时候i初始化。
用法:某个类需要用到一个数组
class A{
    static const int SIZE=10;
    int storage[SIZE}
}
对于不支持的使用枚举代替
 enum {SIZE=10};
 int storage[SIZE];
13 友元如何使用?注意事项
访问类的私有成员开一个后门,能够使得全局函数和其它类的成员函数访问到类的私有成员。
可以有:
友元函数:全局函数
友元类:外部类
友元成员函数:类的某一成员函数
友元关系时授予不是索取:
如果f时A的友元,在A中必须声明。而不是f自称时A的友元。
通常,把友元放在类内部第一行,并且不要加访问控制。它不支持,对称和传递。

#include<iostream>  //拷贝构造函数调用的顺序
using namespace std; class A{
int x;
public:
A(int b=):x(b){cout<<"construt instance :"<<x<<endl;}
A(const A & ar){x=ar.x; cout<<"调用拷贝构造函数了。"<<endl;}
void display(){ cout<<" x :"<<x<<endl;}
~A(){cout<<"析构 :"<<x<<endl;}
};
A global(); //1, 全局对象,比main还早。
void f(){
cout<<"start f():"<<endl;A a();
static A local(); //执行到f内部才开始执行这个 ,在main函数结束后开始析构,和全局相比,它首先析构 }
int main(){
A a();//2 ,执行main
A b(a);//3 ,调用拷贝构造
//b=a;//这个不会调用,因为不是定义b的时候初始化
// A c=b;//这个调用拷贝
f();
return ;
}

07

08