基础
helloworld
#include<iostream>
usingnamespacestd;
intmain()
{
cout<<"hello world"<<endl; //输出文字
system("pause"); // 方便观察结果
return0;
}
头文件.h
写xxx.h时,防止头文件重复包含
#ifndef AAA
#define AAA
#endif
命名空间
- 功能:区分同名变量或函数
- 创建 :namespace name { code}
namespace SortTestHelper{
}
使用方法
using namespace name;
name::变量名/函数;
:: 作用域运算符
3. using name::成员;
指定开放某个特定成员
赋值运算符
十进制运算
运算符 | 说明 | 范例 | 结果 |
= | 将一个表达式的值赋给另一个 | C = A + B | C = A + B |
+= | 相加后再赋值 | C += A | C = C + A |
-= | 相减后再赋值 | C -= A | C = C - A |
*= | 相乘后再赋值 | C *= A | C = C * A |
/= | 相除后再赋值 | C /= A | C = C / A |
%= | 求余后再赋值 | C %= A | C = C % A |
二进制运算
运算符 | 说明 | 范例 | 结果 |
<<= | 左移后赋值 | C <<= 2 | C = C << 2 |
>>= | 右移后赋值 | C >>= 2 | C = C >> 2 |
&= | 按位与后赋值 | C &= 2 | C = C & 2 |
^= | 按位异或后赋值 | C ^= 2 | C = C ^ 2 |
|= | 按位或后赋值 | C |= 2 | C = C | 2 |
输入与输出
字符串的输入
1. cin
cin是C++中最常用的输入语句,当遇到空格或者回车键即停止,处理带空格字符串时容易忽略出错
#include <iostream>
#include <string>
using namespace std;
int main()
{
char a[50];
cin>>a;
cout<<a<<endl;
return0;
}
输入:abcd遇回车输出abcd
缺点:只能输入没有空格的字符串,当输入中含有空格,则只能输出空格之前的字符
输入:"Hello world"输入空格时输入并未停止,遇回车输入停止,输出"Hello",空格及空格后面的均未输出。
2. gets()
可以无限读取,以回车结束读取,C语言中的函数,在C++中运行会产生bug。
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
char a[50];
gets(a);
cout<<a<<endl;
return0;
}
输入:"Hello world"回车结束输入,输出结果为"Hello world"。
3. getline()
若定义变量为string类型,则要考虑getline()函数。用法如下:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string a;
getline(cin,a);
cout<<a<<endl;
return 0;
}
输入:"Hello world"回车结束输入,输出结果为:"Hello world"
4.cin.get
cin.get()函数可以接收空格,遇回车结束输入。
#include <iostream>
using namespace std;
int main()
{
char a[50];
cin.get(a,50);
cout<<a<<endl;
return 0;
}
输入:"Hello world"回车结束输入,输出结果为"Hello world"
5. cin.getline
cin.getline()函数可以同cin.get()函数类似,也可接收空格,遇回车结束输入。
#include <iostream>
using namespace std;
int main()
{
char a[50];
cin.getline(a,50);
cout<<a<<endl;
return 0;
}
输入:"Hello world"回车结束输入,输出结果为"Hello world"
输入一行数字(未知个数),对其进行解析
1、利用“cin + cin.get()”
输入一行,并以空格隔开,空格处理可以留给cin去除,cin.get()主要判断是否换行(输入结束),代码如下:
//存入动态数组
intx;
vector<int>vec;
while (cin>>x)
{
vec.push_back(x);
if (cin.get() =='\n')//下一个为换行符,就不再获取
break;
}
for (inti=0; i<vec.size(); i++) {
cout<<vec[i];
}
//存入静态数组
intx[10005];
inti=0;
intn;
while (cin>>n) {
x[i] =n;
i++;//最后的i值等于输入的个数
if (cin.get() =='\n')
break;
}
for (intj=0; j<i; j++) {
cout<<x[j];
}
eg:输入数据为1 5 6 7,vec中的数据为{1, 5, 6, 7}。
- 一点说明:其中1、5中间有3个空格,getchar()会获取一个字符,不是换行符,现在缓冲区中剩下5 6 7,继续while,cin >> num 会自动会自动处理前面的空格,只获取最后的num。
- 如果获取的是string,把num改为string类型就可以了。(vector的类型也需要改变)。
cin.get()
也可以用getchar()
替换,但getchar()
是C语言的函数
cin是C++的标准输入流,其本身是一个对象,并不存在返回值的概念。
不过经常会有类似于 while(cin>>a) 的调用,这里并不是cin的返回值,应该关注">>"输入操作符,其实是它到底返回了什么
“>>”操作重载函数istream& operator>>(istream&, T &);的返回值,其中第二个参数由cin>>后续参数类型决定。
其返回值类型为istream&类型,大多数情况下其返回值为cin本身(非0值),只有当遇到EOF输入时,返回值为0。
所以会有以下这种cin连续读取的方法
1 cin >> x >> y;
数据类型
数组
有一个数组a,则数组名a就是数组首元素的地址,即
a=&a[0]
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
声明数组
在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:
type arrayName [ arraySize ];
初始化数组
在 C++ 中,您可以逐个初始化数组,也可以使用一个初始化语句,如下所示:
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
int a[26]={0};//初始化全为0
大括号 { } 之间的值的数目不能大于我们在数组声明时在方括号 [ ] 中指定的元素数目。
如果您省略掉了数组的大小,数组的大小则为初始化时元素的个数。因此,如果:
double balance[] = {1000.0, 2.0, 3.4, 7.0, 50.0};
您将创建一个数组,它与前一个实例中所创建的数组是完全相同的。
[ ]的三种作用
- 声明变量的时候有[ ],表示声明的是数组变量
- 函数参数有[ ],此时表示指针
- 地址+[ ],表示下标运算
arr[3]=*(arr+3)
int arr[]={1,2,3};
int a=4;
arr[3]=10;
cout << arr[3];
//输出
//10
上述代码中[]
就是进行了下标运算,数组arr和a依次被定义,他们被安排在同一块内存空间的相邻位置,且都为int型,那么arr[3]实际指向的是a的内存空间,因此a被改写
结构体struct
C++独有
- 声明变量可以不用struct关键字
- 可以放函数成员
- C语言的不可以放函数成员,但是可以放函数地址
- 是一个特殊的类
#include <iostream>
using namespace std;
struct Node {
int i;
//可以放函数成员
void fun() {
cout << "我是struct" << endl;
}
};
int main()
{
Node a;//声明变量可以不用struct关键字
a.fun();
system("pause");
return 0;
}
指针
内存申请与释放
new
——申请空间
//失败返回一个NULL,同C语言的malloc一样
int *p = new int;
int *p = new int(10); //初始化一个值
struct Node{code;};
Node *p = new Node;
int *p = new int[10]; //申请数组空间
- memset(起始地址,设置的值,字节数) 设置内存初始值
- 堆区的空间不像栈区的那种,可以在声明的时候初始化
delete
——释放空间
- delete p; <==> new type;
- delete [ ] p;<==> new type [count];
- delete NULL 是安全的, free(NULL)会崩溃
和C语言申请释放的区别
- new delete 可以触发构造函数和析构函数,C语言则不会触发
- 同时释放一块空间大于一次是不行的
- 指针声明和空间分配方式不同,使用则完全相同,所以说,在C++里,除了对象空间申请和释放之外,用malloc 和 free是完全可以的
指针类型
指针的大小
- 地址+1 是加了一个类型的大小
- 64bit编译器——8字节
- 32bit编译器——4字节
*的三种作用(C/C++)
- 声明变量的时候有*,表示指针变量
int *p
- *+地址,表示地址操作符,对地址存储的值进行操作
- 数字*数字,表示乘法
函数指针
函数存放在内存的代码区域内,它们同样有地址.如果我们有一个 int test(int a) 的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。
int (*fp)(int a); // 这里就定义了一个指向函数(这个函数参数仅仅为一个 int 类型,函数返回值是 int 类型)的指针 fp。
引用变量
概念
- 引用是已定义的变量的别名(另一个名称),两者用法完全一样
- 同typedef一样 是给类型起别名
声明与定义
基本数据类型变量的引用
int b = 12;
int &c = b; //c是b的引用
int &d = b; //d是b的引用
int &e = d; //e是d的引用
- 定义的时候就要初始化
- 引用不能再指向其他的
常量的引用(需要const)
const int &n = 12;
const char &c = 'a';
const float &f = 123.123f;
数组的引用
int a[2];
int (&b)[2] = a;
int a[2][3];
int (&c)[2][3] = a;
方式同数组指针一样
结构体的引用
类型 & 名字 = 结构体实例;
指针的引用
int* p = NULL;
int* (&d) = p;
引用与函数
引用与指针的区别
- 引用声明就要初始化,指针不用初始化,例如可以声明:int *p = NULL;
- 引用不能指向其他变量,指针则可以任意指向。p = &a; p = &d;
- 引用不占存储空间,指针占空间
- 引用效率更高,指针是间接操作
- 引用更安全,指针可以偏移
- 指针更灵活,直接操作地址,指针更通用,C语言和C++都行
&的三种作用
- 声明变量的时候有&,表示引用
- 变量前边加&,表示取地址
- 数&数,表示位与运算
函数
声明,定义
参数缺省值(默认值)
- 全部指定:
int fun(int a = 0, char c = 'b', float f = 12.12 );
- 部分指定:
int fun(int a , char c = 'b', float f = 12.12 );
从右向左逐个指定
如果声明与定义时分开的,那么声明可以有默认值,定义不可以有默认值
函数重载
函数名字相同,参数列表不同
void fun(int a);
void fun(int a, int b);
void fun(char c);
void fun(float f, double d);
系统会根据参数的形式,自动找到要调用的函数
类
class是一种类型,只有在创建对象时才会分配内存空间
类的定义
class 类名
{
};
- 跟结构体是一样的,只不过关键字是class
- 结构体是特殊的类
创建对象
1. 栈区普通对象
CPeople op;
1)显示调用:
Stock food = Stock("360",250,2.5);
2)隐式调用;
Stock food("360",250,2.5);
2. 堆区指针对象(new出来的东西都在堆中)
CPeople* op1 = new CPeople;
调用类内成员
除了静态成员,都要使用对象来调用
栈区普通对象
//对象.成员
op.Test();
堆区指针对象
指针对象要使用指针操作符
new完之后要记得delete
//对象->成员
op1->Test();
delete op1;
访问权限修饰符
- private
类内可见
类内不写访问修饰符,默认是private - protected
类内以及子类可见 - public
类外可见
C++的结构体默认是public - friend(友元)
- 友元可以在类中直接定义,也可以只声明,然后在类外定义
- 友元函数可以访问类的所有成员(包括private、protected)
- 友元函数并不是成员函数
- 相同class的各个objects互为friends(友元)
- 友元函数
friend void fun();
friend int main();
- 友元类
friend class xxx
- 使用protected成员有两种方法
- 继承
- 友元
- 使用private成员只能用友元
- 特点
- 不受访问修饰符影响
- 可以有多个友元
- 缺点:破坏了类的封装性,不是迫不得已不要用
构造函数
类名(参数列表){}
//因为返回的必然是当前类的对象,所以不写即可
创建对象时会自动调用构造函数
- 栈区普通对象
- 堆区指针对象
声明指针并不会调用构造函数,只有new空间的时候才调用
类型
- 默认无参构造
- 什么都不做
- 只要用户声明构造函数,默认的构造函数就没有了
- 无参构造
- 有参数构造
- 通过对象传递
- 可以指定默认值
多个构造函数构成重载
初始化列表
只有构造函数才有初始化列表
数据在构造函数中有两个状态,先是初始化,后是赋值
初始化与赋值的区别
- 初始化是一个变量或者对象产生之时就赋予一个初始值,伴随性质
- 赋值是一个变量或者对象产生之后的任意时刻可以赋予一个值,随意性质
- 对于引用,const:只能初始化,不能赋值
初始化形式
构造函数之后加个冒号:a(1),b(2)
成员初始化顺序,只与声明顺序有关,与初始化列表书写顺序无关
class MyClass
{
public:
int a;
int b;
MyClass() : b(1),a(2)//a先初始化为2,b再初始化为1
{
a=2;//赋值
b=3;
}
};
const
const在函数名前面的时候修饰的是函数返回值
const在函数名后面表示是常成员函数,该函数不能修改对象内的任何成员,只能发生读操作,不能发生写操作。
class Test(){
public:
Test(){}
const int foo(int a);
const int foo(int a) const;
};
我们都知道在调用成员函数的时候编译器会将对象自身的地址作为隐藏参数传递给函数,在const成员函数中,既不能改变this所指向的对象,也不能改变this所保存的地址,this的类型是一个指向const类型对象的const指针。
inline(内联函数)
https://www.runoob.com/w3cnote/cpp-inline-usage.html
解决了一些频繁调用的小函数大量消耗栈空间(栈内存)的问题
定义在类中的成员函数默认都是内联的,关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用
inline const char *num_check(int v)
{
return (v % 2 > 0) ? "奇" : "偶";
}
上面的例子就是标准的内联函数的用法,使用 inline 修饰带来的好处我们表面看不出来,其实,在内部的工作就是在每个 for 循环的内部任何调用 num_check(i) 的地方都换成了 (i%2>0)?"奇":"偶",这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。
参数传递
pass by value
value有多少字节,就将多少字节进栈,不推荐使用
test(int i){}
C语言中是靠指针来解决这个问题的,当value太大,那我们传递它的地址即可
C++中的引用相当于指针,但比指针更快,因此所有参数传递建议都使用引用,返回值亦是如此
pass by reference(to const)
引用相当于给变量起的别名,因此在函数中修改引用的话,外部的value也会改变。
当我们不想value发生改变时,可以使用const关键字
test(int& i){}
test(const int& i){}
操作符重载
在C++中的操作符重载有两种形式,一种是在类内声明public
函数实现操作函重载(这种情况下,操作符是作用在左操作符上的);另一种是在类外声明全局函数实现操作符重载.
操作符就是一个函数,可以通过重载重新定义,且有两种重载方式,一是写成成员函数,二是写成非成员函数(全局函数)
1. 成员函数
在类或结构体内定义成员函数来重载操作符
class complex
{
public:
complex (double r = 0, double i = 0): re (r), im (i) { }
complex& operator += (const complex&);
private:
double re, im;
};
inline complex&
__doapl (complex* ths, const complex& r)
{
ths->re += r.re;
ths->im += r.im;
return *ths;
}
inline complex&
complex::operator += (const complex& r)
{
return __doapl (this, r);
}
complex c1(2, 1);
complex c2(4, 0);
c2+=c1
编译器执行c2+=c1
时,编译器会把操作符作用于左边的c2,即把右边加到左边,所以可以返回引用
所有成员函数的参数列表都携带一个隐藏的this指针
complex::operator += (this,const complex& r)
this指针指向当前函数的调用者(一个对象),即“谁调用我,我就是谁”。那么return *ths
是返回ths所指向的对象(取地址的值),用引用来接收
this不能在参数列表中写出,但在函数内部可以使用
2. 全局函数(无this)
inline complex
operator + (const complex& x, const complex& y)
{
return complex (real (x) + real (y), imag (x) + imag (y));
}
inline complex
operator + (const complex& x, double y)
{
return complex (real (x) + y, imag (x));
}
inline complex
operator + (double x, const complex& y)
{
return complex (x + real (y), imag (y));
}
上面这些函数不可能返回引用,因为返回的是个局部变量,生命周期和所在函数一样,所以我们要直接返回值
temp object 临时对象:
typename ()
匿名临时对象,生命周期只维持一行
1. 重载operator+
#include <iostream>
using namespace std;
class Stu{
public:
int age;
int score;
Stu(){
age=12;
score=666;
}
};
void operator + (Stu& s,int a){
cout << s.age+a << endl;
}
int main()
{
Stu a,
b;
a+2;//14
return 0;
}
2. 重载 <<
cout << c1;
操作符<<作用于左边(C++中操作符都作用于左边,不会作用于右边),且只能重载为全局函数,不能是成员函数
操作符重载一定作用于左边操作符身上