C++ 学习笔记

1,函数指针和指针函数

                       主体(是函数还是指针)    需求
指针函数        函数                                     返回类型为指针
函数指针        指针                                     指向一个函数
//注意:()的优先级大于 *
int* f(int x,int y);     首先是一个函数,返回一个指针
int (*f)(int x,int y);   首先f是一个指针,指向是一个函数
#include <iostream>  
using namespace std;  
//指针函数  
int* add(int x, int y)  
{  
 int c = x + y;  
 return &c;  
}  
int sub(int x, int y)  
{  
 return x + y;  
}  
//函数指针  
int (*f1)(int x, int y);//指向函数  
int* (*f2)(int x, int y);//指向函数指针  
int main()  
{  
 f1 = sub;  
 f2 = add;  
 cout << (*f1)(1, 2) << endl;  
 cout << *(*f2)(1, 2) << endl;  
}  

2,随机数

#include <ctime>
srand((int)time(NULL));
int a = rand()%100 + 20;

3,typedef

作用:给类型起别名

优点:方便

用法:typedef 类型 别名;

#include <stdio.h>
typedef int INT;
typedef struct {
    int a;
    int b;
}p1;
int main()
{
    INT a = 3;
    p1 p[6] = { {.a = 1,.b = 2},
                {.a = 1,.b = 2},
                {.a = 1,.b = 2},
                {.a = 1,.b = 2},
                {.a = 1,.b = 2},
                {.a = 1,.b = 2}};           
    return 0;
}

4,C语言中malloc,calloc,realloc的使用

(1)malloc函数。其原型void *malloc(unsigned int num_bytes);

(2)calloc函数,其原型void *calloc(size_t n, size_t size);

(3)realloc函数和上面两个有本质的区别,其原型void realloc(void *ptr, size_t new_Size)

int *p1 = (int *)malloc(sizeof(int) * 4);
int *p2 = (int *)calloc(4, sizeof(int));
int *p3 = (int *)realloc(p1, sizeof(int) * 5);

//使用calloc开辟二维数组空间
#include <stdio.h>
#include <stdlib.h>
int main() {
	int** p = (int**)calloc(4, sizeof(int));
	if (p == NULL) return;
	for (int i = 0; i < 4; ++i) {
		p[i] = (int*)calloc(4, sizeof(int));
		if (p[i] == NULL) return;
		for (int j = 0; j < 4; ++j) {
			printf("%d ", p[i][j]);
		}
		printf("%\n");
	}
}
/*
0 0 0 0 
0 0 0 0
0 0 0 0
0 0 0 0
*/

5,=default的含义

        在C++11 新标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上 =default 来要求编译器生成构造函数。

class A
{
public:
    A() = default;//需要默认构造
    A(int x);      //有参构造
};

6,文件输入输出

ifstream 读文件

ofstream 写文件

文件模式

in

以读的方式打开

out

以写的方式打开

app

每次写操作前均定位到文件末尾

ate

打开文件后立即定位到文件末尾

trunc

截断文件

binary

以二进制方式进行IO

is_open();//判断文件是否打开

7,常见容器操作

c.push_back()

尾部插入元素

c.push_front()

头部插入元素

c.pop_back()

删除尾部元素

c.pop_front()

删除头部元素

c.erase(p)

删除迭代器p指定的元素

c.erase(b,e)

删除迭代器b和e所指定范围内的元素

c.clear()

删除所有元素。返回void

c.empty()

判断c是否为空

c.resize()

c.resize(n)

c.resize(n,t)

增大或缩小容器

调整c的大小为n个元素

调整c的大小为n个元素,任何新添加的元素都初始化为值t

s.find(a)

查找s中a第一次出现的位置

s.rfind(a)

查找s中a最后一次出现的位置

 8,lambda表达式

[capture list](parameter list)-> return type { function body}

capture list:捕获列表

parameter list:参数列表

return type:返回类型

function body:函数体

注意:可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体;

一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数中使用该变量

        &引用捕获,=值捕获;

int a = 10;
//值捕获
auto f = [10](int x, int y) {return x + y; };
//引用捕获
auto f2 = [&a](int x, int y) {a = 11;return a + x + y; };
cout << "a=" << a << endl;//10
cout << "f(1, 2)=" << f(1, 2) << endl;//3
cout << "f2(1, 2)=" << f2(1, 2) << endl;//14
#include <iostream>
using namespace std;

void test01()
{
	//lambda常用用法
	auto fun1 = []()->int{ return 666; };
	cout << fun1();
	auto fun = [] () {return 666; };
	cout << fun();
}


void test02()
{
	int a1 = 0; int a2 = 1;
	auto fun = [](int x, int y) {return x > y ? x : y; };
	auto fun1 = [a1, a2](int x, int y) {return x > a2 ? "大" : "小"; };

}


void test03()
{
	int sum = 0; int sum2 = 4;//lambda内部不可修改捕获的值
	auto fun0 = [sum]() {return sum; };//值捕获
	auto fun1 = [&sum](){return sum; };//引用捕获
	auto fun2 = [=]() {return sum; };//隐式捕获-值捕获
	auto fun3 = [&](){return sum; };//隐式捕获-引用捕获
	auto fun4 = [=,&sum](){return sum; };//混合  捕获列表的第一个元素必须是一个&或=,此符号制定了默认捕获方式
	sum = 1;
	cout << "fun0: " << fun0() << endl;//sum = 0;
	cout << "fun1: " << fun1() << endl;//sum = 1;
	cout << "fun2: " << fun2() << endl;//sum = 1;

}

void test04()
{
	//可变lambda 再lambda表达式内部可以修改捕获的值
	int sum = 0;
	auto fun = [sum]()mutable {return sum++; };

	//指定lambda返回类型
	auto fun1 = [](int i)->int {return 0; };
	 
}

int main()
{

	test04();
	return 0;	
}

 

9,constexpr和const关键字

const修饰普通类型的变量

const int a = 0;a不可修改

const 修饰指针变量

const 修饰指针变量有以下三种情况。

A: const 修饰指针指向的内容,则内容为不可变量。

B: const 修饰指针,则指针为不可变量。

C: const 修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量。

 

const 修饰成员函数

C++ 成员函数末尾的const

const:常量,在成员函数后面增加一个const。不单要在成员函数声明中增加const,也要在函数定义中增加const。

作用:告诉系统,这个函数,不会修改对象里的任何成员变量的值等等。也就是说,这个成员函数,不会修改类Time的任何状态。

成员函数后面加const的成员函数也称为“常量成员函数”。

const和constexpr区别:

        C++ 11标准中,为了解决 const 关键字的双重语义问题,保留了 const 表示“只读”的语义,而将“常量”的语义划分给了新添加的 constexpr 关键字。因此 C++11 标准中,建议将 const 和 constexpr 的功能区分开,即凡是表达“只读”语义的场景都使用 const,表达“常量”语义的场景都使用 constexpr。

 

可以看到,程序中用 const 修饰了 con_b 变量,表示该变量“只读”,即无法通过变量自身去修改自己的值。但这并不意味着 con_b 的值不能借助其它变量间接改变,通过改变 a 的值就可以使 con_b 的值发生变化。 

 10,运算符重载

        习惯上人们是使用 cin>> 和 cout<< 的,得使用友元函数来重载运算符,如果使用成员函数来重载会出现 d1<<cout; 这种不自然的代码。

#include <iostream>
using namespace std;

class People
{
public:
	friend ostream& operator<<(ostream& is, People& p);//重载<<
	friend istream& operator>>(istream& is, People& p);//重载>>
	bool& operator==(People& p);//重载==
	People& operator+(People& p);//重载+
	People& operator-(People& p);//重载-
	People& operator*(People& p);//重载*
	People& operator/(People& p);//重载/

	People() = default;
	People(int a, int b)
	{
		this->m_A = a;
		this->m_B = b;
	}
	int m_A;
	int m_B;
};

ostream& operator<<(ostream& is, People& p)//类外定义
{
	is << p.m_A << endl;
	is << p.m_B << endl;
	return is;
}

istream& operator>>(istream& is, People& p)
{
	is >> p.m_A;
	is >> p.m_B;
	return is;
}

bool& People::operator==(People& p)//重载==
{
	bool temp = this->m_A == p.m_A && this->m_B == p.m_B;
	return temp;
}

People& People::operator+(People& p)//重载+
{
	this->m_A = this->m_A + p.m_A;
	this->m_B = this->m_B + p.m_B;
	return *this;
}

People& People::operator-(People& p)//重载-
{
	this->m_A = this->m_A - p.m_A;
	this->m_B = this->m_B - p.m_B;
	return *this;
}

People& People::operator*(People& p)//重载*
{
	this->m_A = this->m_A * p.m_A;
	this->m_B = this->m_B * p.m_B;
	return *this;
}

People& People::operator/(People& p)//重载/
{
	this->m_A = this->m_A / p.m_A;
	this->m_B = this->m_B / p.m_B;
	return *this;
}

int main()
{
	People p(1, 2);
	People p2(1, 4);

	//cin >> p;
	//cout << p << endl;
	
	//bool temp = p2 == p;
	//cout << temp<< endl;

	People p3 = p2 / p;
	cout << p3 << endl;
}

 11,为什么父类指针可以指向子类?

        可以通俗的理解,子类可能含有一些父类没有的成员变量或者方法函数,但是子类肯定继承了父类所有的成员变量和方法函数。

所以用父类指针指向子类时,没有问题,因为父类有的,子类都有,不会出现非法访问问题。但是如果用子类指针指向父类的话,一旦访问子类特有的方法函数或者成员变量,就会出现非法。虽然父类指针可以指向子类,但是其访问范围还是仅仅局限于父类本身有的数据,那些子类的数据,父类指针是无法访问的。

使用函数指针实现父类函数调用子类函数的两种方式

父子类关系

        对于继承关系中的父类和子类,我们可以说子类是父类的一种,子类继承了父类的属性和行为。因此,子类可以访问父类的所有非私有成员。相反,父类一般情况下是不能访问子类成员的。然而,我们可以通过一些方法间接的实现父类访问子类,即父类函数访问子类函数。

方法一

        利用多态机制,一个指向子类的父类指针或引用,当调用被子类重写的虚函数时,实际上调用的是子类函数,这是通过多态的方式来实现父类调用子类,该方法需要一个引用或者指针调用虚函数来实现。

方法二

        通过函数指针同样可以实现父类函数访问子类函数

12,延时函数 

#include <ctime>  
void sleep(int time)//1000 = 1s;  
{   
    clock_t head = clock();  
    while (clock() - head <= time)  
    {  
  
    }   
}  

13,时间打印

#include <iostream>  
#include<iomanip>//前置补零  
//#include<time.h>  
#include <ctime>  
#include <stdio.h>  
using namespace std;  
  
//延时函数  
void sleep(int time)//1000 = 1s;  
{
    clock_t head = clock();  
    while (clock() - head <= time)  
    {  
  
    }  
}  
int main()  
{
    while (true)  
    {  
        time_t t;  
        t = time(NULL);  
        tm tt;  
        localtime_s(&tt, &t);  
        cout << tt.tm_year + 1900 << "-"  
            << setw(2) << setfill('0') << tt.tm_mon + 1 << "-" //范围0~11 所以需要加 1  
            << setw(2) << tt.tm_mday    << " "  
            << setw(2) << tt.tm_hour    << ":"  
            << setw(2) << tt.tm_min     << ":"  
            << setw(2) << tt.tm_sec     <<"\r";//覆盖原始信息  
        sleep(1000);//延时更新  
        //getchar();//按 enter 更新  
    }  
      
} 

14,实现暂停效果

getchar();//获取字符

system(“pause”);//按任意键继续

system(“cls”);//清屏操作

 15,C++中using的三种用法

1,导入命名空间

using namespace std;// 导入整个命名空间到当前作用域

using std::cout; // 只导入某个变量到当前作用域

2,指定别名

C++ 11 通过 using 指定别名,作用等同于 typedef,但相比 typedef,逻辑更直观,可读性更好。

typedef int T; // 用 T 代替 int

using T = int; // 用 T 代替 int

3,在派生类中引用基类成员

尽管派生类 对基类是私有继承,但通过 using 声明,派生类的对象就可以访问基类的 proteced 成员变量和 public 成员函数了。

16,sprintf_s(),sprintf()和printf()区别和用法 

  1. printf函数把结果输出。
  2. sprintf函数把结果输出到指定的字符串中。
  3. sprintf_s()是sprintf()的安全版本,通过指定缓冲区长度来避免sprintf()存在的溢出风险
  4. sprintf_s 会检查格式化字符的合法性,而sprintf只会检查其是否是空指针
#include<stdio.h>  
int main(int argc, char *avgv[])  
{  
    char s[40];  
    sprintf(s,"%s%d%c","test",1,'2');  
    //第一个参数就是指向要写入的那个字符串的指针,剩下的就和printf()一样  
    char buff[256];  
    sprintf_s(buff,256, "../cfg/%d_%d.png", i, j);  
    printf("%s%d%c","test",1,'2');  
    //对保存后的字符串输出  
    printf("%s",s);  
    return 0;  
}  

 17,C++ 字符和字符串(string,char)

1, C++ 内置函数(字符,数字转换)

  1. islower(char c) 是否为小写字母
  2. isupper(char c) 是否为大写字母
  3. isdigit(char c) 是否为数字
  4. isalpha(char c) 是否为字母
  5. isalnum(char c) 是否为字母或者数字
  6. toupper(char c) 字母小转大
  7. tolower(char c) 字母大转小

2,C++ 数字字符与数字相互转换

因为ASC码48就是’0’,也就是说’0’的值是48,而后依次是’1’到’9’。这样正好是char型减去48就是它对应的int值  int data = s[i] - 0;或者int data = s[i] - 48;

3,字符串转换为数字:int atoi(char *);

4, 去除string头尾空白函数

string& trim(string &s)   
{  
    if (s.empty()) return s;  
    s.erase(0,s.find_first_not_of(" "));  
    s.erase(s.find_last_not_of(" ") + 1);  
    return s;  
}  

 18,vector 容器去重

1,结合sort和unique函数

        unique()函数将相邻且重复的元素放到vector的尾部 然后返回指向第一个重复元素的迭代器再用erase函数擦除从这个元素到最后元素的所有的元素。

vector<int> vec;  
sort(vec.begin(), vec.end());  
vec.erase(unique(vec.begin(), vec.end()), vec.end());  

 2,利用set的特性

    vector<int> vec;  
    set<int> s(vec.begin(), vec.end());
    vec.assign(s.begin(), s.end()); 

 19,进制转换(二进制,八进制,十六进制)

void 进制表示() {  
    int a = 0b1001;//二进制表示   0b  
    int b = 01001;//八进制表示    0  
    int c = 0x1001;//十六进制表示 0x  
    cout<<"a = " << a << endl;  
    cout<<"b = " << b << endl;  
    cout<<"c = " << c << endl;  
}  
  
void 进制输出() {  
    int a = 13;  
    a = a | 0b10;  
    cout << bitset<10>(a) << endl;//二进制输出  
    cout << hex << a << endl;//十六进制输出  
    cout << oct << a << endl;//八进制输出  
}  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值