C++笔记
一、输入输出
scanf 输入字符串,%c,空格是不会被跳过的
用scanf输入,出现非控制字符,则这些字符会被跳过
scanf("%d %c,%f:%d",&a,&b,&c,&d);
prinft("%d,%c,%f,%d",a,b,c,d);
这时候输入
12 k,3.75:290
// 12和k之间的空格不会被输入(注意%c,空格也算是一个字符)
// , : 不会被输入 (用scanf输入,出现非控制字符,则这些字符会被跳过)
则会输出
12,k,3.750000,290
二、变量
变量就是一个代号,程序运行时系统会自动为变量分配内存空间,于是变量就代表系统分配的那片内存空间,对变量的访问,就是对其代表的内存空间的访问
变量有名字和类型两种属性,不同变量的名字就对应了内存中的不同地址(记不同位置),而变量的类型,决定了一个变量占用多少字节
三、c++基本数据类型
1.int long short char的最高位(最左边)是符号位,为1则表示负数,为0则表示非零数。
2.一个字节等于8个比特,即8个二进制位
3.int 字节数4 取值范围
E = -2^{31} 到 2^{31}-1
因为有4个字节,一个字节8位,那么就有32位,最高位是符号位,也就是有31位,那么
4.sizeof(变量名) sizeof(类型名) 求占用字节数
5.有符号整数二进制表示形式
非负数: 1 0000 0000 0000 0001
负数: -1 1111 1111 1111 1111
将一个负整数表示为二进制的方法
- 设置符号位为1
- 其余位等于绝对值取反再加1
比如-1 设置符号位为1,绝对值为1,1的二进制表示形式
1000 0000 0000 0001 (设置符号位为1)
再取反
1111 1111 1111 1110 (符号位不用取反)
再加1
1111 1111 1111 1110
1
---------------------
1111 1111 1111 1111
所以-1的二进制表示形式就是1111 1111 1111 1111
6.数据类型的自动转换
int a = 18.99; // a为18
double d = 30; // d 为30.0
int k = 'a'
printf("%d", k) // 97
整形数据也可以转换为字符型数据,但是有个问题,整形数据有2 4 8字节,字符型数据只有一个字节,装不下下去,这时候的处理是:
只会留下最右边的一个字节(第0位到第8位),其他字节丢弃
四、友元函数、友元类
https://www.runoob.com/cplusplus/cpp-friend-functions.html
五、c++闭包,Lambda 函数与表达式
六、c++指针
- 指针就是一个变量,声明如下:
Type *pr;
这个Type必须是一个有效的c++数据类型,例如int等
int main()
{
int a = 10;
int *prt;
prt = &a;
cout << &a << endl;
// 输出指针所指变量的地址
cout << prt << endl;
// 输出指针所指变量的值(加*就是取值)
cout << *prt << endl;
// 输出存储这个指针变量的地址(加&就是取地址)
cout << &prt << endl;
return 0;
}
结果:
0x7ffd566597ec
0x7ffd566597ec
10
0x7ffd566597e0
- 指针是可以进行算术运算的
比如一个int类型的指针ptr,如果这时候指针指向的地址是 0xbfa088b0,那么 ptr++ 后就是 0xbfa088b4 ,原因是ptr++后他会指向下一个整数位置,即当前位置往后移4个字节,之所以是4个字节,因为这个是int类型的指针,int类型是4个字节
- 指针数组:就是一个存储指针变量的数组
声明
int *ptr[MAX]
一定要指定数据的个数MAX,不然编译不过
- 数组指针
double balance[50];
balance是一个指向&balance[0]的指针
- 指针常量
int * const p;
int a,b;
int * const p=&a //指针常量
//那么分为一下两种操作
*p=9;//操作成功
p=&b;//操作错误
因为是常量了,所以他的值不允许修改,又因为是指针,所以他的值就是地址
但是可以修改这个地址的值
- 常量指针
在定义指针变量的时候,数据类型前用const修饰,被定义的指针变量就是指向常量的指针变量
const int *p = &a; //常量指针
int a,b;
const int *p=&a //常量指针
//那么分为一下两种操作
*p=9;//操作错误
p=&b;//操作成功
因为常量指针本质是指针,并且这个指针是一个指向常量的指针,指针指向的变量的值不可通过该指针修改,但是指针指向的值(地址)可以改变。
- 指向指针的指针
一般的指针是指向一个变量的地址
指向指针的指向,就是一个指向指针变量的指针
定义
int **var;
#include
using namespace std;
int main ()
{
int var;
int *ptr;
int **pptr;
var = 3000;
// 获取 var 的地址
ptr = &var;
// 使用运算符 & 获取 ptr 的地址
pptr = &ptr;
// 使用 pptr 获取值
cout << "var 值为 :" << var << endl;
cout << "*ptr 值为:" << *ptr << endl;
cout << "**pptr 值为:" << **pptr << endl;
return 0;
}
// 输出
var 值为 :3000
*ptr 值为:3000
**pptr 值为:3000
七、引用
引用变量:其实就是一个变量的别名
int i = 88;
int &r = i;
这时候r变量就是引用变量,我们可以对r变量进行赋值
r = 8;
这时候i和r变量的值都改成8了,原因就是r其实就是变量i的别名,所以对r操作,也就相当于对i操作
引用VS指针
引用很容易与指针混淆,它们之间有三个主要的不同:
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
- 引用必须在创建时被初始化。指针可以在任何时间被初始化。
把引用作为参数
// 函数定义
void swap(int& x, int& y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
把引用作为返回值
double& setValues( int i )
{
return vals[i]; // 返回第 i 个元素的引用
}
函数返回引用,其实也可以看作是返回的一个指针,所以可以直接把这个函数做为左值
setValues(1) = 20.23;
当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
int& func() {
int q;
//! return q; // 在编译时发生错误
static int x;
return x; // 安全,x 在函数作用域外依然是有效的
}
八、结构体
struct type_name {
member_type1 member_name1;
member_type2 member_name2;
member_type3 member_name3;
.
.
} object_names;
type_name结构体类型名称
object_names 是可以定义多个结构体变量
结构体作为函数参数
void printBook( struct Books book )
{
cout << "书标题 : " << book.title <
指向结构的指针
struct Books *struct_pointer;
九、重载运算符和重载函数
函数重载
函数名相同,形参不同
不能通过返回类型的不同来重载函数
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};
运算符重载
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的
Box operator+(const Box&);
大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
如果是类的成员函数,那么就不需要两个形参了,另一个用this就可以
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
// Box1 调用 + 函数,所以重载operator+中的this就是box1
Box3 = Box1 + Box2;
不是所有的运算符都可以重载
C++ 动态内存
了解动态内存在 C++ 中是如何工作的是成为一名合格的 C++ 程序员必不可少的。C++ 程序中的内存分为两个部分:
- 栈:在函数内部声明的所有变量都将占用栈内存。
- 堆:这是程序中未使用的内存,在程序运行时可用于动态分配内存。
new 和 delete 运算符
C++命名空间
定义命名空间
// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
调用
// 调用第一个命名空间中的函数
first_space::func();
您可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。
using namespace std;
C++模板
函数模板
#include
#include
using namespace std;
template
inline T const& Max (T const& a, T const& b)
{
return a < b ? b:a;
}
int main ()
{
int i = 39;
int j = 20;
cout << "Max(i, j): " << Max(i, j) << endl;
double f1 = 13.5;
double f2 = 20.7;
cout << "Max(f1, f2): " << Max(f1, f2) << endl;
string s1 = "Hello";
string s2 = "World";
cout << "Max(s1, s2): " << Max(s1, s2) << endl;
return 0;
}
类模板
#include
#include
#include
#include
#include
using namespace std;
template
class Stack {
private:
vector elems; // 元素
public:
void push(T const&); // 入栈
void pop(); // 出栈
T top() const; // 返回栈顶元素
bool empty() const{ // 如果为空则返回真。
return elems.empty();
}
};
template
void Stack::push (T const& elem)
{
// 追加传入元素的副本
elems.push_back(elem);
}
template
void Stack::pop ()
{
if (elems.empty()) {
throw out_of_range("Stack<>::pop(): empty stack");
}
// 删除最后一个元素
elems.pop_back();
}
template
T Stack::top () const
{
if (elems.empty()) {
throw out_of_range("Stack<>::top(): empty stack");
}
// 返回最后一个元素的副本
return elems.back();
}
int main()
{
try {
Stack intStack; // int 类型的栈
Stack stringStack; // string 类型的栈
// 操作 int 类型的栈
intStack.push(7);
cout << intStack.top() <
C++ Struct和类
C++ Struct和类
多态
允许将子类类型的指针赋值给父类类型的指针