C++与数据结构常见知识点

1.sizeof运算符

(1) 定义
sizeof是一个操作符(operator)。
其作用是返回一个对象或类型所占的内存字节数。(返回一条表达式或者一个类型名字所占的字节数。——C++ Primer)

(2) 语法

sizeof有三种语法形式:

1) sizeof (object); //sizeof (对象)
2) sizeof object; //sizeof 对象
3) sizeof (type_name); //sizeof (类型)

对象可以是各种类型的变量,以及表达式(一般sizeof不会对表达式进行计算)。
sizeof对对象求内存大小,最终都是转换为对对象的数据类型进行求值。
sizeof (表达式); //值为表达式的最终结果的数据类型的大小

(3) 数组的sizeof

数组的sizeof值等于数组所占用的内存字节数。
注意:1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。
2)当数组为形参时,其sizeof值相当于指针的sizeof值。

参考链接

2. 虚基类

定义:当在多条继承路径上有一个公共的基类,在这些路径中的某几条汇合处,这个公共的基类就会产生多个实例(或多个副本),若只想保存这个基类的一个实例,可以将这个公共基类说明为虚基类。(百度百科
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为
虚基类
(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

3.虚函数

虚函数的作用是实现动态绑定的,也就是说程序在运行的时候动态的的选择合适的成员函数。

虚析构函数

4.const

如果const出现在号右边,则表示指针自身是常量(顶层const);如果const出现在号左边,则表示被指物是常量(底层const);如果const出现在*号两边,则表示被指物和指针都是常量。

**顶层const(指针常量)**用来标明一个变量其本身是一个不可更改的常量。内置类型的const为顶层const。对于指针,被顶层const修改后,不可更改指针指向的对象。一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。

const int i = 1;//顶层const
int *const p = &i;//顶层const,不可更改p指向的对象

**底层const(常量指针)**用来标明一个指针或引用所指向的对象是一个不可更改常量。对于指针和引用,被底层const修改后,不可通过指针或引用修改指针指向的对象值。(可以通过其他方式修改其值)

int i = 1;
const int *p = &i;//底层const
*p = 3;//错误,不可通过被const修饰的指针修改对象值
i = 3;//正确,const指针只影响修饰的对象

执行拷贝操作时,顶层const对于拷贝操作无影响

const int i = 1;
int m = i;//i具有顶层const对于拷贝操作无影响。

但是底层const不可忽略。执行拷贝操作时,拷入与拷出对象必须具有相同的底层const,或者两对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之不行。

int i = 1;
const int *p = &i;//正确,非常量转换为常量
int *q = p;//错误,常量不可转换为非常量
const int *r = p;//正确,等号两边都具有底层const

5.引用

引用 (& )是标识符的别名;引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。
定义一个引用时,必须同对它进行初始化使指向已存在的象。
在这里插入图片描述

  1. 把引用作为参数

在这里插入图片描述在这里插入图片描述

int i = 17;
int&  r = i;
double& s = d;

在这些声明中,& 读作引用。因此,第一个声明可以读作 “r 是一个初始化为 i 的整型引用”,第二个声明可以读作 “s 是一个初始化为 d 的 double 型引用”。

  1. 把引用作为返回值

通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。C++ 函数可以返回一个引用,方式与返回一个指针类似。当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。

参考

6.前置++和后置++的区别

从操作符重载的角度,看i++和++i的区别,是一个比较好的切入点。

class Age
{
public:
	Age& operator++() //前置++
	{
		++i;
		return *this;
	}
 
	const Age operator++(int) //后置++
	{
		Age tmp = *this;
		++(*this);  //利用前置++
		return tmp;
	}
 
	Age& operator=(int i) //赋值操作
	{
		this->i = i;
		return *this;
	}
 
private:
	int i;
};

从上述代码,我们可以看出前置++和后置++,有3点不同:

**返回类型不同:**前置++的返回类型是Age&,后置++的返回类型const Age。这意味着,前置++返回的是右值,后置++返回的是右值
左值和右值,决定了前置++和后置++的用法。

**形参不同:**前置++没有形参,而后置++有一个int形参,但是该形参也没有被用到。很奇怪,难道有什么特殊的用意?其实也没有特殊的用意,只是为了绕过语法的限制 。

**代码不同:**前置++的实现比较简单,自增之后,将this返回即可。需要注意的是,一定要返回this。后置++的实现稍微麻烦一些。因为要返回自增之前的对象,所以先将对象拷贝一份,再进行自增,最后返回那个拷贝。

**效率不同:**如果不需要返回自增之前的值,那么前置++和后置++的计算效果都一样。但是,我们仍然应该优先使用前置++,尤其是对于用户自定义类型的自增操作。前置++的效率更高,理由是:后置++会生成临时对象。

参考

7.左值/右值

添加链接描述

有些变量既可以当左值右可以当右值。

左值(Lvalue) →→ Location

表示内存中可以寻址,可以给它赋值(const类型的变量例外)

右值Rvalue) →→ Read

表示可以知道它的值(例如常数)

左值和右值从字面意思来看,就是等号的左右两边的意思,但其实不是这样的,左值是有名字的,是可以取到地址的,而右值是没有名字的,是不可以取地址的。其实可以简单的理解为凡是可以取到地址的变量都是左值。,左值具有右值的一切属性。左值和右值的提出是为了避免非必要的拷贝和临时对象而产生的。

8.单引号和双引号

单引号是字符型, 双引号是字符串型

‘a’表示是一个字符,"a"表示一个字符串相当于’a’+’\0’;

9.getline

此函数可读取整行,包括前导和嵌入的空格,并将其存储在字符串对象中。

1.使用标准输入输出操作符读写string对象。

int main()

{

   string s;
   cin>>s;
   cout<<s<<endl;
   return 0;

}

这样的话,如果输入字符串带有空格(非字符串首部、尾部),则只能输出空格前部分。

而且,读取并忽略开头所有的空白字符。

2.读入未知数目的string对象

string的输入操作符和内置类型的输入操作符一样,也会返回所读的数据流。因此,可以把输入操作作为判断条件:

int main()
{
   string s;
   while(cin>>s)
   cout<<s<<endl;
   return 0;
}

当未到达文件尾且未遇到无效输入,则执行循环体,并将读取到的字符串输出到标准输出。如果到达文件尾,则跳出while循环。

3.使用getline读取整行文本

getline(cin,strings);
getline从输入流的下一行读取,并保存读取的内容到string中,但不包括换行符。即便它是第一个字符,也会终止读入,并返回。

int main()
{
   string s;
   while(getline(cin,s))
       cout<<s<<endl;
   return 0;
}

getline(cin, inputLine);
在这里插入图片描述

10.读取数量不定的输入数据

			#include <iostream> 
			int main()
			{
			     int  sum = 0, value = 0;
			     //读取数据直到遇到文件尾,计算所有输入的值的和
			     while(cin >> value)//此处是重点
			      {
			          sum += value;
			       }
			      cout << " sum is: "<< sum <<endl;
			      return 0;
			}

while循环的判据就是表达式cin >> value,这个表达式代表从标准输入中读取下一个数,保存在value中。输入运算符返回的是其左侧的对象,即cin。故这个循环检测的实际上是cin。

当使用一个istream(即cin)对象作为条件时,其效果是检测流的状态。若流有效,没有遇到错误,那么检测成功。当遇到**文件结束符或者无效输入(**本例中无效输入为非整型)时,cin会处于无效状态,循环会停止。

在这里插入图片描述

11.string常用函数

C++ 中string.erase() 的用法

12.new()

“new”是C++的一个关键字,同时也是操作符。

定义:
new 类型名T(初始化参数列表)

功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以
初值。

结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。
释放内存操作符delete

delete 指针p

功能:释放指针p所指向的内存。p必须是new操作的返回值。

new其实就是告诉计算机开辟一段新的空间,但是和一般的声明不同的是,new开辟的空间在堆上,而一般声明的变量存放在栈上。通常来说,当在局部函数中new出一段新的空间,该段空间在局部函数调用结束后仍然能够使用,可以用来向主函数传递参数。另外需要注意的是,new的使用格式,new出来的是一段空间的首地址。所以一般需要用指针来存放这段地址。具体的代码如下:

#include <iostream>
using namespace std;
 
int example1()
{
  //可以在new后面直接赋值
  int *p = new int(3);
  //也可以单独赋值
  //*p = 3;
 
  //如果不想使用指针,可以定义一个变量,在new之前用“*”表示new出来的内容
  int q = *new int;
  q = 1;
  cout << q << endl;
 
  return *p;
}
 
int* example2()
{
  //当new一个数组时,同样用一个指针接住数组的首地址
  int *q = new int[3];
  for(int i=0; i<3; i++)
    q[i] = i;
 
  return q;
}
 
struct student
{
  string name;
  int score;
};
 
 
student* example3()
{
  //这里是用一个结构体指针接住结构体数组的首地址
  //对于结构体指针,个人认为目前这种赋值方法比较方便
  student *stlist = new student[3]{{"abc", 90}, {"bac", 78}, {"ccd", 93}};
 
  return stlist;
}
 
 
 
int main()
{
  int e1 = example1();
  cout <<"e1: "<< e1 << endl;
 
  int *e2 = example2();
  for(int i=0; i<3; i++)
    cout << e2[i] << " ";
  cout << endl;
 
 
  student *st1 = example3();
 
  for(int i=0; i<3; i++)
    cout << st1[i].name << " " << st1[i].score << endl;
 
 
 
  return 0;
}

13.C++中对象new出来和直接声明的区别

(1)首先,最直观的,new出来的对象需要使用指针接收,而直接声明的不用。例如 A* a=new A() 与A a()。

(2)new出来的对象是直接使用堆空间,而局部声明一个对象是放在栈中。
new出来的对象类似于申请空间,因此需要delete销毁,而直接声明的对象则在使用完直接销毁。

(3)new出来的对象的生命周期是具有全局性,譬如在一个函数块里new一个对象,可以将该对象的指针返回回去,该对象依旧存在。而声明的对象的生命周期只存在于声明了该对象的函数块中,如果返回该声明的对象,将会返回一个已经被销毁的对象。

(4)new对象指针用途广泛,比如作为函数返回值、函数参数等``。

14.函数参数为void和没有参数的区别

C语言中的函数在声明和定义的时候可以没有参数。众所周知,如果函数被声明和定义为void f(void);则说明该函数在调用时不能传入任何参数。而如果函数被声明和定义为void f();则说明该函数在调用时候可以传入任意参数。
如果函数无参数,那么应声明其参数为 void

C++中没有区别。
在 C++语言中声明一个这样的函数:

int function(void) { return 1; }

则进行下面的调用是不合法的:function(2);

因为在 C++中,函数参数为 void的意思是这个函数不接受任何参数。

15.const与static、const在函数前与函数后区别

const在函数前与函数后区别

在普通的非 const成员函数中,this的类型是一个指向类类型的 const指针。可以改变this所指向的值,但不能改变 this所保存的地址。

在 const成员函数中,this的类型是一个指向 const类类型对象的 const指针。既不能改变 this所指向的对象,也不能改变 this所保存的地址。

任何不会修改数据成员的函数都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。

const 成员函数的声明看起来怪怪的:const关键字只能放在函数声明的尾部,大概是因为其它地方都已经被占用了。
关于Const函数的几点规则:

const对象只能访问const成员函数,而非const对象可以访问任意的成员函数,包括const成员函数.

const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的.

const成员函数不可以修改对象的数据,不管对象是否具有const性质.它在编译时,以是否修改成员数据为依据,进行检查.

然而加上mutable修饰符的数据成员,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的

举例:

1、int GetY() const;

2、const int * GetPosition();

对于1

该函数为只读函数,不允许修改其中的数据成员的值。

对于2

修饰的是返回值,表示返回的是指针所指向值是常量。

static和const的区别

能不能同时用static和const修饰类的成员函数?

不可以。

解释:1:C++编译器在实现const的成员函数时为了确保不能通过该函数修改对象的状态,会在函数中添加一个隐式的参数 this指针(类名 const* const this)。一个成员函数为static的时候,该函数是没有this指针的。此时const的用法和static是冲突的。

解释2:语意矛盾。static的作用是表示该函数只作用在类型的静态变量上,与类的实例无关;const的作用是确保函数不能修改类的实例的状态,与类型的静态变量无关。因此不能同时使用。

16.void *memset(void *s, int ch, size_t n);

memset是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。

函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。

memset:作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。

memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度.

17.数组声名后不初始化,数组里的值都是0吗?

1、全局/静态数组
如果申明的是全局/静态数组,系统会把数组的内容自动初始化为0。

2、局部数组
如果申明的是局部数组,数组的内容会是随机的,不一定是0。如函数内声明:
int Func()
{
char szTest[10]; //此时内容是随机的
memset(szTest, 0, sizeof(szTest));
}
3、成员数据
如果申明的是类的成员数组,数组的内容是随机的,不一定是0。一般在类的构造函数内用memset初始化为0。

发布了23 篇原创文章 · 获赞 10 · 访问量 380

猜你喜欢

转载自blog.csdn.net/weixin_43892514/article/details/104157262