首页 > 编程语言 >C++笔记整理

C++笔记整理

时间:2023-01-02 14:03:28浏览次数:59  
标签:const 函数 int 笔记 String C++ 整理 data 指针


把自己印象笔记中所记录的一些C++知识点整合了一下,可用于面试前对C++知识的快速回顾。

不过并不全,只是自己笔记中的摘要,重要的还是系统和踏实地学习。

每个知识点不分顺序。

1.typeid是什么

typeid用于类的类型检查

同一类型

指针类

D d;

C *p=d;

typeid(*p)==typeid(D)则满足

返回类型是type_info


2.define的一些注意点


#define a 10
void

foo();
main(){

printf
(
"%d.."
,a);

foo();

printf
(
"%d"
,a);
}
void

foo(){

#undef a

#define a 50
//只影响这句话之后的内容
}


答案输出10..10


另外,不管是在某个函数内,还是在函数外,define都是从 定义开始直到文件结尾 ,所以如果把foo函数放到main上面的话,则结果会是50 ,50

#define a 10

void foo();

void prin();



int main() {

prin();

printf ( "%d " , a);

foo();

printf ( "%d " , a);



}

void foo() {

#undef a

#define a 50

}

void prin() {


printf ( "%d " , a); //此处的a已经被替换


}

上面代码输出 50 10 10,可以看出define只是在预处理阶段将a替换为相应数值,具体替换的值,只与define在文件中的位置有关,与是否在函数内无关。


3.重载,重定义,重写是什么?

1.重写(override):


      父类与子类之间的多态性。子类重新定义父类中有相同名称和参数的虚函数。


1)被重写的函数不能是static的。必须是virtual的(即函数在最原始的基类中被声明为virtual )。


2)重写函数必须有相同的类型,名称和参数列表(即相同的函数原型)


3)重写函数的访问修饰符(public...)可以不同。尽管virtual是private的,派生类中重写改写为public,protected也是可以的


 


2.重载(overload):


      指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。


 


3.重定义(redefining):


      子类重新定义父类中有相同名称的非虚函数(参数列表可以不同)。


 


重写与重载的区别 (override) PK (overload)


1、方法的重写是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系。


2、重写要求参数列表相同;重载要求参数列表不同。


3、重写关系中,调用那个方法体,是根据 对象的类型(对象对应存储空间类型)来决定;


      重载关系,是根据 调用时的实参表与形参表来选择 方法体的。




4.C++中的空类,默认产生哪些类成员函数?

class Empty
{
public:
Empty(); // 缺省构造函数
Empty( const Empty& ); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=( const Empty& ); //
赋值运算符
Empty* operator&(); //
取址运算符
const Empty* operator&() const; //
取址运算符 const
};


5.运算符重载

规则:

不可重载"." 或"*",这2个是用来访问成员的

不可重载“::”,作用域分辨符的操作数是类型

不可重载“?”,是三目运算符

返回类型 operator 运算符 (形参) {

     函数体

}

成员函数的运算符重载比非成员函数少一个形参

因为自身this就是一个

重载自加运算符后可以返回对象的引用, 以方便在表达式中连续使用。


举个例:


cout<<其实是重载了<<这个操作符,cout是一个ostream的对象。如果不返回引用,cout<<a<<b<<endl;


若不是引用,则cout<<a就不是一个输出流对象,就无法继续接b在屏幕上输出了。 


就不可以一起写了。如果返回自身的引用 cout<<a之后,返回身身的引用,后面可以继续接b了。就是这个意思吧。好多重载操作符,作用其实都是这样的。

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

class z;
ostream & operator<< (ostream &out,z z1);

class z{
public:
int real,imag;
z(int r=0,int i=0):real(r),imag(i){};
z operator+ (const z &z1) const;
z operator- ();//无参数,前置负号
z &operator++ () ; //前置,即++z
z operator++ (int) ; //后置,即z++
friend ostream & operator<< (ostream &out,z z1);
void show();
};

//友元函数,不用加::
ostream & operator<< (ostream &out,z z1)
{
out<<"("<<z1.real<<","<<z1.imag<<")";
return out;
}

z z::operator+ (const z &z1) const
{
return z(this->real+z1.real,imag+z1.imag);
}

z &z::operator++ ()
{
real++;
imag++;
return *this;
}

z z::operator++ (int )
{
return z(real++,imag++);
}

z z::operator- ()
{
return z(-real,-imag);
}
void z::show()
{
printf("(%d,%d)\n",real,imag);
}
int main()
{
z a(1,5),b(3,1);
cout<<"a+b=";
(a+b).show();
(a++).show();
a.show();
(++b).show();
b.show();
(-b).show();
cout<<"a="<<a<<endl;
}



6.模板类和类模板的区别

模板定义:模板就是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型定义为参数,从而实现了真正的代码可重用性。模版可以分为两类,一个是函数模版,另外一个是类模版。

Template <class或者也可以用typename T>

返回类型 函数名(形参表)


{//函数定义体 }



//T可以被任何字母或者数字代替。  
template <class T>
T min(T x,T y)
{
return(x<y)?x:y;
}



模板类和重载函数一起使用时:


     两者一起使用时, 先考虑重载函数 , 后考虑模板类 ,如过再找不到,就考虑类型转换,可能会带来精度的变化。


    


类模板:

template <class T>  
class Base
{
public :
T a ;
Base(T b) {
a = b ;
}
return a ;} //类内定义
void setA(T c);
};

template <class T> //模板在类外的定义
void Base<T>::setA(T c)
{
a = c ;
}



7.static 和 const 的作用

static关键字至少有下列5个作用:

(1) 函数体内变量 。函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值 ;

(2) 模块内全局变量 。在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;

(3) 模块内函数 。在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

(4) 类内成员变量 。在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

(5) 类内成员函数 。在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。 

const关键字至少有下列n个作用:

(1)欲 阻止一个变量被改变 ,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;

(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;

(3)在一个函数声明中, const可以修饰形参 ,表明它是一个输入参数,在函数内部不能改变其值;

(4)对于类的成员函数,若指定其为const类型,则表明其是一个 常函数,不能修改类的 成员变量 ;

(5)对于类的成员函数,有时候必须 指定其返回值为const类型 ,以使得其返回值不为“左值”。例如:

const classA operator*(const classA& a1,const classA& a2);

operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错:

classA a, b, c;

(a * b) = c; // 对a*b的结果赋值

操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。



8.String类的实现

编写类String的构造函数、析构函数和赋值函数,已知类String的原型为:

class                   String         { 

public :

String( const char *str = NULL); // 普通构造函数

String( const String &other); // 拷贝构造函数

~ String( void ); // 析构函数

String & operator =( const String &other); // 赋值函数

private :

char *m_data; // 用于保存字符串

};

参考答案


//普通构造函数

String::String( const char *str) {

if (str==NULL) {

m_data = new char [1]; // 得分点:对空字符串自动申请存放结束标志'\0'的空

//加分点:对m_data加NULL 判断

*m_data = '\0' ;

}

else {

int length = strlen (str);

m_data = new char [length+1]; // 若能加 NULL 判断则更好

strcpy (m_data, str);

}

}

// String的析构函数

String::~String( void ) {

delete [] m_data; // 或delete m_data;

}

//拷贝构造函数

String::String( const String &other)     // 得分点:输入参数为const型 {

int length = strlen (other.m_data);

m_data = new char [length+1];      //加分点:对m_data加NULL 判断

strcpy (m_data, other.m_data);

}

//赋值函数

String & String::operate =( const String &other) // 得分点:输入参数为const型 {

if ( this == &other)    //得分点:检查自赋值

return * this ;

delete [] m_data;      //得分点:释放原有的内存资源

int length = strlen ( other.m_data );

m_data = new char [length+1];   //加分点:对m_data加NULL 判断

strcpy ( m_data, other.m_data );

return * this ;          //得分点:返回本对象的引用

}


9.assert的作用

参数:Expression (including pointers) that evaluates to nonzero or 0.(表达式【包括指针】是非零或零)

原理:assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。



用法总结:


1) 在函数开始处检验传入参数的合法性


如:


int resetBufferSize(int nNewSize)

{

//功能:改变缓冲区大小,

//参数:nNewSize 缓冲区新长度

//返回值:缓冲区当前长度

//说明:保持原信息内容不变 nNewSize<=0表示清除缓冲区

assert
(nNewSize >= 0);

assert
(nNewSize <= MAX_BUFFER_SIZE);

...

}


2) 每个assert只检验一个条件 , 因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败


不好:  assert (nOffset>=0 && nOffset+nSize<=m_nInfomationSize);


好:  assert (nOffset >= 0);


     assert (nOffset+nSize <= m_nInfomationSize);




3) 不能使用改变环境的语句, 因为 assert 只在DEBUG个生效,如果这么做,会使用程序在真正运行时遇到问题


错误:  assert (i++ < 100)


这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。


正确:  assert (i < 100);


         i++;


4) assert和后面的语句应空一行 ,以形成逻辑和视觉上的一致感


5)有的地方, assert 不能代替条件过滤



ASSERT 只有在Debug版本中才有效,如果编译为Release版本则被忽略掉。(在C中, ASSERT 是宏而不是函数),使用 ASSERT “断言”容易在debug时输出程序错误所在。


   而 assert ()的功能类似,它是ANSI C标准中规定的函数,它与 ASSERT 的一个重要区别是可以用在Release版本中。


使用 assert 的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。


在调试结束后,可以通过在包含#include < assert .h>的语句之前插入 #define NDEBUG 来禁用 assert 调用,示例代码如下:



void* memcpy(void *dst, const void *src, size_t count)      
{
//安全检查
assert( (dst != NULL) && (src != NULL) );

unsigned char *pdst = (unsigned char *)dst;
const unsigned char *psrc = (const unsigned char *)src;

//防止内存重复
assert(!(psrc<=pdst && pdst<psrc+count));
assert(!(pdst<=psrc && psrc<pdst+count));

while(count--)
{
*pdst = *psrc;
pdst++;
psrc++;
}
return dst;
}




10.函数指针的用法

int max(int a,int b)
{
cout<<a+b<<endl;
return a+b;
}


int (*p)(int,int)=max;


int main()
{
(*p)(1,2); //都可以!
p(1,2);
}
函数指针


Int and(int a,int b){return a+b;}

int (*p)(int ,int );

p=and;

int c=(*p)(1,2);


11.枚举变量

枚举元素不是字符常量也不是字符串常量,使用时不要加单、双引号。

只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量

enum e{a,b,c=5,d,e} x;

int main()
{
char *s="abcde";
x=(enum e)d; //d代表6,a代表0,b代表1
switch(x)
{
case a:cout<<"a"<<endl;break;
case b:cout<<"b"<<endl;break;
case c:cout<<"c"<<endl;break;
case d:cout<<"d"<<endl;break;
case e:cout<<"e"<<endl;break;
}
}




12.构造函数 析构函数 是不是虚函数

1. 为什么构造函数不能为虚函数?

   虚函数的调用需要虚函数表指针,而该指针存放在对象的内容空间中;若构造函数声明为虚函数, 那么由于对象还未创建,还没有内存空间,更没有虚函数表地址用来调用虚函数 ——构造函数了。

2. 为什么析构函数可以为虚函数,如果不设为虚函数可能会存在什么问题?

  首先析构函数可以为虚函数,而且当要使用基类指针或引用调用子类时,最好将基类的析构函数声明为虚函数,否则可以存在内存泄露的问题。

  举例说明:

  子类B继承自基类A; A *p = new B; delete p;

  1) 此时,如果类A的析构函数不是虚函数,那么delete p; 将会仅仅调用A的析构函数,只释放了B对象中的A部分,而派生出的新的部分未释放掉。

  2) 如果类A的析构函数是虚函数,delete p; 将会先调用B的析构函数,再调用A的析构函数,释放B对象的所有空间。

  补充: B *p = new B; delete p;时也是先调用B的析构函数,再调用A的析构函数


13.特殊类型变量

如果一个变量被频繁使用,需保存在寄存器中,因为寄存器的速度要比内存快的许多。在早期的编译器中需要手动定义为register型,但是后来编译器可以自动将调用次数多的变量放入寄存器中。

auto: 默认的分配类型。一般不需要手动声明了,C++11特性; 

auto s="abc",ss="edf" //auto变成string

auto a=1,*p=2 //相当于aoto变成int

auto a=1,c="abc"  //出错,2者类型不一致


static:静态分配内存。变量在整个作用域内是全局变量。


extern : 声明为外部变量;在函数的外部定义变量;


Volatile

修饰符,修饰后的变量可以防止被编译优化,每次取值时会逐语句取值(多线程)


14.二重指针寻址

#include <stdio.h>

void f( char **p){

*p += 2 ;


}






main()

{

char *a[] = { "123" , "abc" , "456" },**p;

p = a;

f(p);

printf( "%s\r\n" ,*p);


}



输出3



*p+=2;就相当于*p=*p+2;

其中*p指向字符串“123”的第一个元素,即‘1’,指针p向后移两个元素的地址,即指向‘3’


而*(p+2)才是基于p每次向后移一个字符串的长度,即*(p+2)指向“456”

15.STL一级容器

STL中一级容器是指, 容器元素本身是基本类型, 非组合类型。(vector, deque, list.)

set, multiset中元素类型是pair<key_type, key_type>;

map, multimap中元素类型是pair<key_type, value_type>;


16.多重继承的优缺点

多继承或者继承都是为了实现类的重用和封装,优点就是减少代码量,实现算法抽象等;缺点(多继承)就是容易造成名字空间冲突(尤其对于MFC类或者其他同一个基类的一般不能使用多继承),或者所称的菱形继承。建议看看《设计模式》这本书,一般可以通过其他方法(聚合等)实现多继承的话应该避免多继承。

17.动态联编(多态)

动态联编就是程序在运行的时候知道该调用哪个函数,而不是编译阶段,所以这个机制应该是由虚函数支持的,即运行时的多态。

基类的某个成员函数声明为虚函数,派生类继承,而且同样重写该函数,那么当声明一个派生类的指针或者引用时,它所调用的函数是由该指针指向的对象确定的,这就是动态联编

多态:指当不同的对象收到相同的消息时,产生不同的动作

编译时多态:函数重载、运算符重载——静态绑定

运行时多态:虚函数机制——动态绑定

C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。


18.转义字符

\0 空字符(NULL) 000 

\ddd 任意字符 三位八进制 (不可有大于7的数字!)

\xhh 任意字符 二位十六进制 

一般转义字符,如‘\b’,由两个字符表示,其实代表一个字符,这个代表退格字符


19.重载的注意事项

重载只要求函数名相同,参数类型和个数不同,对返回值类型不做要求

不能重载‘.’,因为‘.’在类中对任何成员都有意义,已经成为标准用法。   


不能重载 ?: ,因为这个运算符对于类对象来说没有实际意义,相反还会引起歧义 

还有::

20.指针数组

int fun(int *p[4]),

p是包含4个指针的数组。

int a[4][4]作为形参不符合,因为他指向的内容是数组,而不是指针

int **a 二级指针,指针指向的内容还是指针,对的

int D[4][8]  对应的形参为

int(*s)[8] ( 数组指针,每个都指向对应的数组的每列)



int D[][8]

1、int(*p)[4];------ ​​ptr​​ 为指向含4个元素的一维整形数组的指针变量(是指针)  对应4个元素数组的指针


2、int *p[4];-------定义指针数组p,它由4个指向整型数据的指针元素组成(是数组)


对应指针的数组


3、int(*)[4];--------实际上可以看作是一种数据类型。也就是第一个(int(*p)[4];)


21.数组取地址的问题

a[]={1,2,3,4,5}

此时,a指代数组的首地址

而&a指代的是也是数组首地址,但是会把数组a看作一个整体

(int *)(&a+1)则指数组最后一个元素的下一个

以下代码的输出是(2  , 5)

int         a[        5        ]={        1        ,        2        ,        3        ,        4        ,        5        };

int *ptr=( int *)(&a+ 1 );


printf( "%d,%d" ,*(a+ 1 ),*(ptr- 1 ));



int a[5]={0,1,2,3,4};

cout<<*(a++)<<endl; //此处a++是错的

作为数组名,a是一个 常量指针 ,a指向的内容可改,但是a指针的指针地址不可改。

应该让int *p=a,那么即可执行p++

当a传入函数时,退化为指针,那么就可以执行a++了



char *b="abc"

则*b="123"是错的,内容不可改,指向可改。

但b=c是对的。


22.读文件时的定位问题

#include <stdio.h>

main() {

FILE * fp;

int i,a[ 6 ]={ 1 , 2 , 3 , 4 , 5 , 6 },k;

fp = fopen( "data.dat" , "w+" );

for (i= 0 ;i< 6 ;i++) {


fseek(fp,0L, 0 ); //移动到文件开头,偏移0



fprintf(fp, "%d\n" ,a[i]); //



rewind (fp); //也是回到文件开头


fscanf(fp, "%d" ,&k);

}

fclose(fp);

printf( "%d\n" ,k);


}

的输出结果是6


则程序的输出结果是?


如果来看解析的话,估计这道题难点在于fseek和rewind两个函数不太了解。


fseek(文件,偏移量,类别)改变指针函数,,其中类别为文件开头 0,文件当前位置 1,以及文末 2。

所以,fseek(fp,0L,0)就是把文件指针fp移到里开头0字节的地方,即开始位置。


rewind相当于fseek(fp,0L,0),由此可见出题人内心腹黑,强行多考一个函数。


代码循环内流程如下:移到开始位置->写一个数字->移到开始位置->读一个数字(恰恰是刚才写的那个) 循环。


23.结构体大小判断

32位和64位系统的区别在于long和指针,32位下他们是4字节,64位下他们是8字节
short都是2字节,float都是4字节,double都是8字节,long long都是8字节
short int  2字节
long long int 8字节
遵循两条原则:一、结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
 二、结构体大小必须是所有成员大小的整数倍。
struct A{
 longa1;
 shorta2;
 inta3;
 int*a4;
};
例如求上面这个结构体A的sizeof大小,在64位下
a1的移量为0,长度为8
a2的偏移量为0+8=8,长度为2
a3的偏移量为8+2=10,不符合int长度4的倍数,故偏移量变成12,,长度为4
a4的偏移量为12+4=16,长度为8
故总大小为16+8=24,24是所有成员大小的整数倍,故A的大小为24


23.结构体大小判断

#include <stdio.h>

main() {

FILE * fp;

int i,a[ 6 ]={ 1 , 2 , 3 , 4 , 5 , 6 },k;

fp = fopen( "data.dat" , "w+" );

for (i= 0 ;i< 6 ;i++) {


fseek(fp,0L, 0 ); //移动到文件开头,偏移0



fprintf(fp, "%d\n" ,a[i]); //



rewind (fp); //也是回到文件开头


fscanf(fp, "%d" ,&k);

}

fclose(fp);

printf( "%d\n" ,k);


}

的输出结果是6


则程序的输出结果是?


如果来看解析的话,估计这道题难点在于fseek和rewind两个函数不太了解。


fseek(文件,偏移量,类别)改变指针函数,,其中类别为文件开头 0,文件当前位置 1,以及文末 2。


所以,fseek(fp,0L,0)就是把文件指针fp移到里开头0字节的地方,即开始位置。


rewind相当于fseek(fp,0L,0),由此可见出题人内心腹黑,强行多考一个函数。


代码循环内流程如下:移到开始位置->写一个数字->移到开始位置->读一个数字(恰恰是刚才写的那个) 循环。





标签:const,函数,int,笔记,String,C++,整理,data,指针
From: https://blog.51cto.com/u_15806016/5983657

相关文章

  • 多项式乘法学习笔记
    多项式乘法给定两个多项式\(A(x)=\sum_{i=0}^{n-1}{a_ix^i}\)\(B(x)=\sum^{m-1}_{i=0}{b_ix^i}\)求\(C(x)=A(x)\timesB(x)=\sum_{i=0}^{n+m-1}{c_ix^i}\)......
  • C++ priority_queue使用方法
    以leetcode1081题为例,https://leetcode.cn/problems/number-of-orders-in-the-backlog/classSolution{public:intgetNumberOfBacklogOrders(vector<vector<int......
  • C/C++高级语言程序设计课程设计任务书[2022-01-02]
    C/C++高级语言程序设计课程设计任务书[2022-01-02]高级语言程序设计课程设计任务书课程设计名称 中文:高级语言程序设计课程设计英文:ComputerProgrammingBasicCompreh......
  • 在线视频项目学习笔记(四)—前台分类相关
    一、分类列表接口 即在分类模块显示所有的一级分类以及其子类。   注意:上图中在返回的对象中封装了一个List二、根据分类ID查询分类的具体信息 ......
  • C/C++课程设计题目[2023-01-02]
    C/C++课程设计题目[2023-01-02]选题1:考勤信息管理系统某公司对员工的出勤采用计算机管理,为该公司设计一个员工考勤信息管理程序。系统包括三类用户:管理员,考勤员,普通职员......
  • 笔记本 AUTO模式是什么意思
    相关文章电脑AUTO是什么意思https://zhidao.baidu.com/question/1645428036244428940.html##简介电脑屏幕、显示器上的按键“AUTO”是用来自动校对屏幕显示的位置的按......
  • C/C++通讯录管理程序[2023-01-02]
    C/C++通讯录管理程序[2023-01-02]问题描述:编写一个简单的通讯录管理程序。通讯录记录有姓名,地址(省、市(县)、街道),电话号码,邮政编码等四项。基本要求:程序应提供的......
  • C++小型公司工资管理系统[2023-01-02]
    C++小型公司工资管理系统[2023-01-02]题目14“小型公司工资管理系统设计”1、问题描述某公司需要存储雇员的编号、姓名、性别、所在部门,级别,并进行工资的计算。其中,雇......
  • C/C++停车场管理系统[2023-01-02]
    C/C++停车场管理系统[2023-01-02]项目七:停车场管理系统1、教学内容提供停车场地的管理,分为月租车和临时停车两大类。场地分为月租车停放区域和临时车辆停放区域两大块......
  • C++ string 基本用法
    一、C++string的创建方式1、string的头文件#include<iostream>//等价于C语言中的#include<stdio.h>#include<cstring>#include<string.h>//以上两种都是C语言的string头......