C++学习笔记

第一周 从C到C++

第一节课 函数指针

就是一个指向函数的指针,赋值后也可以直接当函数用

定义形式

返回值类型名 (*指针变量名)(参数类型1,参数类型2……)

int (*pf)(int, char)
使用方法

#include<stdio.h>
int PrintMin(int a, int b){
    pass
}
int main(){
    void (*pf)(int,int);//先声明一个函数指针
    int x = 4,y = 5;
    pf = PrintMin;//把之前建立的函数赋值给这个指针,就可以把这个指针当函数用了
    pf(x,y);
    return 0;
}

函数指针与qsort函数

qsort是c语言自带的一个快速排序函数,但是它的一个参数是另一个函数,即需要自己编写两个数比较的规则.
void qsort(void *base, int nelem, unsigned int width, int (*pfCompare)(const void *, const void *))
*pfCompare:调用pfCompare比较两个元素,若返回负整数,则元素1应排在前面,反之后面
*base:待排序数组的起始地址,即数组名
nelem:待排序数组的元素个数
width:待排序数组的每个元素的大小,以字节为单位
实例
调用qsort函数,将一个unsigned int 数组按照个位数从小到大排序,比如8,23,15,排序后为23,15,8

#include<stdio.h>
#include<stdlib.h>
int myCompare(const void *a, const void *b){
        unsigned int *first , *second;
        first = (unsigned int *)a;
        second = (unsigned int *)b;//此处不能直接用*b,因为传入的a为const void *类型,见注释
        return (*first%10)-(*second%10);
}
#define NUM 5
int main(){
        unsigned int num[NUM] = {8,123,11,10,4};
        int (*pfCompare)(const void *, const void *);
        pfCompare = myCompare;
        qsort(num,NUM,sizeof(unsigned int),pfCompare);//之后数组已被排序修改
        for(i = 0;i<NUM;i++)
                print("%d ",num[i]);//遍历打印数组
        return 0;
}
  • 数组初始化用{}
  • 函数名字和函数指针类型是匹配的
  • void *为不确定类型指针,void *赋给其他类型的变量时,需要进行强制类型转换
  • 定义myCompare时参数不能为unsigned int *a,不然不能赋值给pfCompare并传给qsort

第二节课 命令行参数

使用方法
使用命令行参数时,需要在main函数加入参数,这样通过命令行执行程序时main会接收两个参数,这两个参数是os自动根据命令行处理好后交给main的。

int main(int argc, char *argv[]){
....
}

argc:用命令行执行程序时,参数的个数,包括程序文件名本身(可以用来作遍历数组时的循环边界)
argv:参数指针数组,每个元素都是char *类型,比如argv[0]就是第一个参数,即程序文件名,argv[1]就是第二个参数,随便用户传入的是什么就是什么第

第三节课 位运算

按位与 &
作用:清零某些位并保持其他位不变(清零对应0,不变对应1),或者判断某些位是0是1(和1与)
按位或 |
作用:将某些位置1其他位不变(置1和1或,不变和0或)
按位异或 ^
作用:将某些位取反,其他位不变(任何数与1异或取反,与0异或不变)
特点:

  • 若a^b=c,则c^b=a,且c^a=b,穷举法可证
  • 可以用异或运算交换变量值,而不用临时变量
int a = 5, b = 7;
a = a^b;
b = b^a;
a = a^b;

穷举法可证
按位非 ~
单目运算符,就是取反码。用法: ~21
左移运算符 <<
作用:a<<b,a左移b位,但并不会改变a的值,只给出结果。相当于乘法,移动时高位丢弃,低位添0
右移运算符 >>
作用:除法,除不尽往小取整。右边低位丢弃,左边高位添符号位

第四节课 引用

概念
int &a = ba为b的引用,也就是说a与b等价,改变a的值就会改变b的值,反之同理

  • a定义为b的引用之后就一直为b的引用,就算赋值a=c,也不会把a变成c的引用,而是把c的值赋给a
  • a在声明时需要用变量初始化,不能用常量

用法
其实算是c++独有用法,用来代替指针,简化函数使用时的写法

void swap(int &a,int &b){
	int temp;
	temp=a;a=b;b=temp;
}
int n1,n2;
swap(n1,n2)

此处不用传入n1,n2的指针也能直接通过函数改变n1,n2的值,因为传进去时相当于int &a=n1;int &b=n2;
常引用
const int & a = b;即为常引用
特点:不能通过常引用修改变量。也就是说,不能再直接改变a的值,但可以直接改变b的值

第五节课 const关键字和常量

定义常量
c++最好用const定义常量而不是用c的define,因为const是一个类型const int value = 23;
定义常量指针
const int * p = &n类似于常引用,不可以通过常量指针修改

  • 不能把常量指针(有const的)赋值给非常量指针,但反过来可以int *x;x=p;//报错但强制类型转换后可以x=(int *)p;
  • 调用函数时,就相当于先做一个赋值操作,把传入的参数赋值给形参给函数内使用。

第六节课 动态内存分配

用法1 只分配一个变量的内存
P = new T;P是变量指针,T是类型名,于是为P分配了sizeof(T)的内存
eg.int * p = new int; *p = 5;
用法2 分配一个数组的内存
相当于动态声明了一个数组
P=new T[N];N可以是表达式,P为数组名,即数组起始地址,可以用P[n]获取数组元素

new运算符的返回值类型:
new T与new T[N]都返回T *,即new int 返回int *类型=>int * p = new int ;两边类型相同

释放一个内存空间
delete 指针 即可。
int * p = new int;delete p;
释放动态分配的数组
delete []指针
int * p = int[20];delete []p;

第七节课 内联函数与运算符重载

内联函数

用法:函数定义时前面加inline即可

inline int Max(int a,int b){
	pass
}

原理:节省函数调用指令,直接把函数体与传入参数结合,处理后成为一段代码加入原代码
优点:节省函数调用指令,减少函数调用的开销,对于短函数,重复执行时效率较高
缺点:可执行程序代码变长体积变大

函数重载

概念:函数名相同,参数个数或参数类型不同。编译器根据传入参数确定使用哪个函数

第八节课 函数参数缺省

即python中的默认参数,在定义时赋值即可

第二周 类和对象初探

前两节太简单,略去

第三节课 类的使用初探

类的成员函数声明方法

普通方法:直接在类里声明同时定义

class CRectangle(){
	public:
		int w,h;
	void Init(int w_, int h_){
	w = w_;
	h = h_;
	}
	int Area(){
	return w*h;
	}
};

特殊方法:在类定义里只声明函数,可以在类外定义函数,用双冒号::

class CRectangle{
	public :
		int w,h;
		int Area();
};
int CRectangle::Area(){
	return w*h;
};

调用类内成员的方法

  • 对象名.成员名
  • 指针->成员名CRectangle *p1 = &r2;p1->w=5;
  • 引用名.成员名:引用其实就相当于起别名,所以实际跟第一种相同

第四节课 类成员的可访问范围

private: 只能在成员函数内被访问
public: 可以在任何地方被访问
protected: 指定保护成员

缺省为私有成员

对象成员的访问权限

类的成员函数内部
  • 当前对象的全部属性、函数
  • 同类其他对象的全部属性、函数
类的成员函数以外的地方
  • 只能够访问该类的公有成员(public)

第五节课 构造函数

作用:名字与类名相同,不能有返回值,在对象生成时自动调用,对对象初始化

构造函数与初始化函数的区别:__init__函数不是必须要有的,而且初始化函数必须自己调用才会去初始化,构造函数在对象生成的时候就自动被调用了

类的初始化

  • 可以直接 类名 对象名;eg.Complex cl;
  • 也可以 类名 *对象指针 = new 类名;eg.Complex *pc = new Complex;
  • 当有自定义的构造函数时,实例化时要传入参数。eg.Complex cl(3) or Complex *pc = new Complex(3);
  • 当实例化数组类时,参数可以用{}赋值传入.eg.CSample array[2]={4,5};
    Note:只声明指针并不会初始化对象.Complex *p;并不会调用构造函数

第三周 类和对象进阶

第一节课 复制构造函数

构造函数的一种,用于复制对象,如果不自己编写会有个默认的。
自己编写的复制构造函数可以不执行复制操作。
eg.

Complex c1;
Complex c2(c1);//调用c2的默认复制构造函数,把c1复制到c2

注意
自己编写复制构造函数时,要加&引用:

class Complex{
		public:
				double a,b;
		Complex(Complex  & c){//自己编写的复制构造函数,参数不能用Complex c
				cout<<"Copy constructor called"<<endl;
		}
}

应用场景

(1)用一个对象去初始化另一个同类对象

Complex c2(c1);

(2)函数的参数是类A,则在调用函数时形参A会调用自己的复制构造函数(而不会去调用原来的构造函数,即仅调用复制构造函数

class A{
		public:
		A(){};
		A(A &a){
		cout<<"copy constructor called"<<endl;
		}
}

void func(A a1){};
int main(){
		A a2;
		func(a2);//此时会调用a1的构造函数
}

(3)函数的返回值是A,返回时,A的复制构造函数被调用(复制构造函数调用时,即代表原本的构造函数不会被调用)

A func(){
	A b(4);
	return b;
}
则调用func()时,是A的复制构造函数被调用,A的复制构造函数的参数是b

第二节课 析构函数

作用

处理对象消亡时的善后工作,比如释放用户申请的内存等
(缺省的析构函数不会释放用户申请的内存)

形式

  • 函数名同类名
  • 在函数名前有 “~”
  • 无参数和返回值
  • 一个类只有一个析构函数

调用时机

  • main函数结束时未消亡的对象自动调用析构函数消亡
  • 使用delete时自动调用析构函数
  • 对象所在作用域结束后消亡
  • 全局变量、静态变量会在程序结束前消亡

第三节课 静态成员变量和函数

关键字:static

特点

  • 普通的成员变量,在对象建立时也会多一份,即每个对象有一份。
  • 静态成员变量,所有同class的对象共享一份
  • sizeof()会忽略静态变量
  • 静态成员不具体作用于哪一个对象

访问静态成员的方法

PrintTotal()是一个静态成员函数
(1)类名::成员名
CRectangle::PrintTotal();
(2)对象名.成员名
CRectangle c;
c.PrintTotal();
(3)指针->成员名
CRectangle * r = &c;
r->PrintTotal();
(4)引用.成员名
CRectangle & ref = c;
ref.PrintTotal();

设置静态成员的目的

把和某些类紧密相关的全局变量和该类联系在一起,易于维护和理解

注意

(1)静态成员变量需要进行全局声明,即在main之前声明。
eg.
int CRectangle::nTotalNumber = 0;
int CRectangle::nTotalArea = 0;
int main(){
…………
}
(2)静态成员变量如果没有public关键字默认为私有变量,不能在外面通过类名::成员直接访问
(3)在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数,否则说不清楚是访问的哪一个对象的。

第四节课 封闭类

什么是封闭类?

包含成员对象的类
也就是说,类在定义时,成员变量有其他类的对象,这样的类叫做封闭类
eg.

class CTyre{ }//轮胎类
class CEngine{ }//引擎类
class CCar{
	private:
		int price;
		CTyre tyre;
		CEngine engine;
}//CCar类的成员变量里有轮胎类和引擎类,则CCar为封闭类

新的成员变量初始化方法

初始化列表

class CTyre{
	int width;
	public:
		CTyre(int w):width(w){};//即在写构造函数时,用冒号+成员变量(传入参数){}的方式定义构造函数
}
//当然构造函数也可以在类的外边定义,用双冒号::即可,这样就是下面的形式
CTyre::CTyre(int w):width(w){};
//这样实例化对象时直接传入参数即可
CTyre tyre(3);

Note

在定义封闭类时,构造函数要明确成员对象(即成员变量中的其他类)是如何初始化的,即构造函数接收相应参数再传给他们的构造函数,否则编译会报错

第五节课 this指针

相当于python中的self,是非静态成员函数隐藏的一个参数,指向对象本身
note:
为什么是非静态
因为静态成员函数是所有这个类的对象共有的,所以用this不知道指向的是哪个对象
this 指针有什么用?

  • 可以return *this,返回对象本身
  • c++编译时可以看做先翻译成c再编译,成员函数翻译成c时,变成:成员函数(对象类型 *this, 其他参数)

第六节课 常量对象和成员函数

常量对象

实例化时,在定义对象前加const关键字,则实例化后的对象为常量对象,对象的值一经初始化不可修改

常量成员函数

成员函数定义时,在函数说明后面加const关键字
eg.void Sample::GetValue() const {};

特点

  • 常量成员函数执行期间,不能修改成员变量的值,不能调用其他非常量成员函数(否则其他成员函数可能修改成员变量的值),静态成员变量/函数除外(因为静态成员变量/函数本质上说不属于这个对象)
  • 常量对象不能执行非常量成员函数
  • 同名成员函数加const算重载

常引用

(1)为什么要用对象的引用?
因为如果直接用对象的话,会运行对象的复制构造函数,增加程序开销
eg.

class Smaple{};
void PrintfObj(Sample o){};//直接用对象做形参
void PrintfObj(Sample & o){};//用对象的引用做形参

(2)为什么要用常引用?
当我们不希望传入的对象被修改时,则用常引用,即加const关键字
vodi PrintfObj(const Sample & o){};

第四周 运算符重载

第一节课 运算符重载的基本概念

什么是运算符重载?

就是使普通的运算符能有函数的作用,本质上就是函数重载
比如可以让加号“+”能直接使两个类的对象相加
程序编译时,遇到有运算符的操作其实本质上是对运算符函数的调用,操作数就是函数的参数

如何定义

返回值类型 operator 运算符(形参表){
		……
}

eg.

//有一个复数类Complex
//现在把+号重载,使它能让两个Complex相加,返回相加后的Complex
Complex operator +(const Complex & a,const Complex & b){
	return Complex(a.real+b.real, a.imaginary+b.imaginary)
}
Complex a(1,2), b(2,3), c;
c=a+b;

Note:
当运算符重载为成员函数时,只需要有后面那个参数,因为第一个参数默认为对象本身,函数定义时直接把成员变量拿来用就可以了,即a.real变为real即可

第二节课 赋值运算符“=”的重载

Note:赋值运算符只能重载为成员函数
这一节最好自己把代码打一遍,好好理解一下

赋值与初始化

注意,假设已定义了String类,同时重载了赋值运算符“=”
此时,可以使用

String s;
s = "Hello";

但是,不能使用String s2="Hello";因为这条语句中的“=”是算作初始化而不是赋值,初始化时调用的是构造函数,而不会去考虑其运算符重载成员函数。

第八周 STL-1

第一节课 STL概述

STL全称 Standard template library,标准模板库
容器: 可容纳各种数据类型的通用数据结构,就是类模板
迭代器: 可用于依次存取容器中的元素,类似于指针
算法: 用来操作容器中元素的函数模板

vector 头文件

一个动态数组,一般会分配比需要更多的空间
不超过已分配的空间大小时,在末尾插入或删除,都是O(1)
若超过大小,则要重新分配空间,并把之前的数据复制过来,变成O(n)
在前面插入或删除为O(n)

deque 头文件

双向队列,相比于vector,在队列两端增删元素都是O(1)

list 头文件

双向链表
在已找到元素位置时,增删为O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值