C++期末复习(自用)

一、指针和指针变量

我们在定义一个变量 a 时,变量 a 本质上代表一个存储单元,CPU通过该存储单元的地址访问该存储单元中的数据。
所以a本来代表两个值:存储单元的地址和储单元中的数据。于是就有了二义性。
为了消除这种二义性,C语言规定a表示存储单元中的数据,&a表示存储单元的地址。
a存储单元中的数据可以是一个普通数值,也可以是另一个存储单元的地址,
比如:a = &b; 语句就是将b的存储单元的地址存入a存储单元中。

关于“指针”和“指针变量”比较严格的说法是这样的:
系统为每一个内存单元分配一个地址值,C/C++把这个地址值称为“指针”。如有int i=5;,存放变量i的内存单元的编号(地址)&i被称为指针。
“指针变量”则是存放前述“地址值”的变量,也可以表述为,“指针变量”是存放变量所占内存空间“首地址”的变量(因为一个变量通常要占用连续的多个字节空间)。比如在int i=5;后有一句int *p=&i;,就把i的指针&i赋给了int *型指针变量p,也就是说p中存入着&i。所以说指针变量是存放指针的变量。

(1) int* p=&a; //定义指针变量:此时为初始化指针变量,int* p定义了一个指向int类型指针p(我们使用星号把p声明为指针),并初始化p使其指向int类型的变量a,这里&num中的&是取地址操作符,当&作用于一个对象上时,它返回了该对象的地址。
(2) p //解引用 :在这里星号的作用是解引用(这里解引用的意思可能不太好理解,正确的说法应该是星号运算符给出指针变量p中存储的地址上存储的值(或者说是给出指针p指向的地址上存储的值))
由于在C语言中所有的变量都需要先定义声明后使用,要求p对应的存储单元中的数据一定是另一个存储单元的地址,于是,** a表示另一个存储单元中的数据。(这里* 操作符为解引用操作符,它返回指针p所指的对象(左值)。我们可以对*p赋值(对左值赋值),从而改变p所指的地址上所保存的值)
(3)int *p=&a; //定义整型指针变量§保存整型地址值
*p=100; //间接访问符(解引用)通过p间接访问变量a的值并把a的值赋为100
(4)int v2=0,*q=&v2; 等价于 int v2=0,*q; q=&v2;

p++和++p
*p++是指针p所指的值然后指针加一;
*(p++)与上面一样;
(*p)++是指针p所指向的变量的值加一;
在这里插入图片描述

#include <iostream>
using namespace std;

void inc(int x) {//传值调用,x只是形参,函数结束后即被销毁
	x++;
	cout << x << endl;
}
void dec(int& x) {//传址调用,真正修改了指针所指向内容的值
	x--;
	cout << x << endl;
}

int main() {
	int a = 1;
	//int* p = a;//error:类型不匹配
	int* q = &a;
	int& p = a;

	cout << q << endl;//q是指针变量,其值是a的地址
	cout << *(&a) << endl;//*(&a)=a
	cout << *q << endl;//a的值
	cout << p << endl;//p就是a的别名,含义相等
	cout << &p << endl;//等于&a
	cout << endl;

	inc(a);
	cout << a << endl;
	dec(*q);
	cout << a << endl;

	int b[3] = { 1,2,3 };
	int* r = b;
	cout << *r << *r + 1 << *(r + 1);

	return 0;
}

二、左值和右值

lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。
rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是不在内存中占有确定位置的表达式。
假设我们定义了一个整形变量并且给它赋值:

int var;
var = 4;

赋值运算符要求一个lvalue作为它的左操作数,当然var是一个左值,因为它是一个占确定内存空间的对象。另一方面,下面的代码是无效的:

4 = var;        //ERROR!
(var + 10) = 4; //ERROR!

常量4和表达式var+1都不是lvalue(它们是rvalue)。它们不是lvalue,因为都是表达式的临时结果,没有确定的内存空间(换句话说,它们只是计算的周期驻留在临时的寄存器中)。因此给它们赋值没有语意-没有地方给它们赋值。

但,不是所有的对函数调用结果赋值都是无效的。比如,C++的引用(reference)让这成为可能:

int globalvar = 20;

int& foo(){
    return globalvar;
}

int main(){
    foo() = 10;
    return 0;
}

这里foo返回一个引用,这是一个左值,所以它可以被赋值。实际上,C++从函数中返回左值的能力对于实现一些重载运算符时很重要的。一个普遍的例子是在类中为实现某种查找访问而重载中括号运算符 []。

三、函数和函数指针

函数和函数指针

四、static关键字

static关键字用法详解

五、构造函数和析构函数

构造函数和析构函数详解

C++类成员函数参数列表冒号之后的内容:
如下代码中bookNo(s), unit_sold(n), revenue(p * n)这一部分,
这是构造函数初始值列表。
作用:为新创建的对象的数据成员赋初值。
构造函数初值是成员名字的一个列表,括号内的是初始值
如果初始值列表中没有给出全部成员的初始值,那么没有给值的成员就会按照默认的构造函数的形式来初始化。

Sales_data(const string &s, unsigned &n, double p) :
    bookNo(s), unit_sold(n), revenue(p * n){}

六、重载操作符operator

重载操作符(operator)介绍

七、堆与栈区别

堆与栈区别

八、重载(overload)/覆盖(Override)/隐藏(hide)

重载overload,这个概念是大家熟知的。在同一可访问区内被声名的几个具有不同参数列的(参数的类型、个数、顺序不同)同名函数,程序会根据不同的参数列来确定具体调用哪个函数,这种机制就是重载。重载不关心函数的返回值类型,即返回类型不同无法构成重载。此外,C++ 中的const成员函数也可以构成overload。
总结一下重载的特征:
  1、处在相同的空间中,即相同的范围内;
  2、函数名相同;
  3、参数不同,即参数个数不同,或相同位置的参数类型不同;
  4、const成员函数可以和非const成员函数形成重载;
5、virtual关键字、返回类型对是否够成重载无任何影响。

覆盖override,是指派生类中存在重新定义的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体(花括号中的部分)不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫做覆盖,特征是:
1、不同的范围(分别位于派生类与基类);
2、函数名字相同;
3、参数相同;
4、基类函数必须有virtual关键字。

针对上述两个概念,还有一个隐藏hide。所谓的隐藏,指的是派生类类型的对象、指针、引用访问基类和派生类都有的同名函数时,访问的是派生类的函数,即隐藏了基类的同名函数。隐藏规则的底层原因其实是C++的名字解析过程。在继承机制下,派生类的类域被嵌套在基类的类域中。派生类的名字解析过程如下:
  1、首先在派生类类域中查找该名字。
  2、如果第一步中没有成功查找到该名字,即在派生类的类域中无法对该名字进行解析,则编译器在外围基类类域对查找该名字的定义。
总结一下隐藏的特征:
1、如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
2、如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

九、静态成员函数和常成员函数

静态成员:不管这个类创建了多少个对象,静态成员在内存中只保留一份,静态成员用来解决同一个类中不同对象之间数据成员和函数的共享问题。

1、静态成员函数是用关键词static声明的成员函数,它属于整个类而并不属于类的某个对象。

类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。

由于静态函数是独立于类对象而存在的,因此静态成员函数中没有this指针。所以静态成员函数不可以直接访问类中的非静态成员,但是可以直接访问静态成员。

若要访问非静态成员时,必须通过参数传递的方式得到相应的对象,再通过对象来访问。

class B{ 
private: 
    int x; 
    int y; 
    static int count; 
public: 
    B():x(0), y(0){ 
        count++; 
    } 
    B(int xx, int yy):x(xx), y(yy){ 
        count++; 
    } 
    static int getObjCount(); 
}; 
int B::count = 0; 
int B::getObjCount(){ 
    return count; 
} 
int main() 
{ 
    cout << B::getObjCount() << endl; 
    B b1; 
    B b2(10, 20); 
    cout << b1.getObjCount() << endl; 
    cout << b2.getObjCount() << endl; 
    cout << B::getObjCount() << endl;

   B *p = new B;
   cout << p->getObjCount()<< endl;
    return 0; 
} 

静态数据成员的类型可以是所属类的类型,而普通数据成员则不可以。普通数据成员的只能声明为所属类类型的 指针或引用。举例如下:

class base{ 
public : 
static base _object1;//正确,静态数据成员 
base _object2;//错误 
base *pObject;//正确,指针 
base &mObject;//正确,引用 
}; 

2、常成员函数

返回类型 成员函数名 (参数表) const ;

在定义时使用const关键字修饰的用于访问类的常对象函数,成为常成员函数。

常成员函数不能更新对象的数据成员,也不能调用该类中的没有用const修饰的成员函数,但可以被其他成员函数调用。

常对象只能调用常成员方法,而不能调用其他的成员函数。

const关键字可以用于对重载函数的区分

class CDate
{
private:
 int y;
 int m;
 int d;
public:
 int year()const;
 int month() const;
 int day(){return d++ ;}
 //int day()const {return d;}
 int AddYear(int i){return y+1;}

};

int CDate::year()const
{
 return y;
}
int CDate::month()const
{
 return m;
 //return m++;   错误:常成员函数中不能更新类的数据成员
}

int main()
{
 CDate const date;
 //int day = date.day(); 错误常对象不能调用非常成员函数
 int year = date.year();
}

十、Template

类模板

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值