一、基础语法
1.0 hello again
#include <iostream>
using namespace std; //使用标准命名空间
int main() //主函数
{ //语句块中定义的变量具有局部定义域
cout << "Hello, world!" << endl; //输出流
//endl 插入换行符,并刷新流
system("pause"); //程序不会自动跳出
return 0;
}
C++语言特性
- 静态类型的、编译式的
- 支持过程化编程、面向对象编程、泛型编程。
1.1 基本语法
标识符:①以字母或_
开始 ②由数字、字母、_
组成 ③不能是关键字或操作符
空白符:空格、制表符、换行符、回车符和注释的总称
调试:设置断点,F5,,F11 逐语句,F10 逐过程
注释://
单行注释,/*...*/
多行注释
1.2 数据类型
signed
unsigned
变量类型: | bool | char | short | int | long | long long | float | double |
---|---|---|---|---|---|---|---|---|
字节数 | 1 | 1 | 2 | 4 | 4 | 8 | 4 | 8 |
.
显式数据类型转换:int(z)
(int)z
static_cast<int>(z)
完全等价
.
转义字符: | \n | \t | \v | \b | \r | \\ |
---|---|---|---|---|---|---|
含义 | 换行 | 水平制表符 | 垂直制表符 | 退格 | 回车 | 字符\ |
1.3 运算符
- 常量
- 整数常量
- 八进制:以
0
开头 - 十六进制:以
0x
开头
- 八进制:以
- 浮点型常量(近似存储)
- 字符串常量
- C风格字符串常量:以
\0
结尾的字符序列 - string类对象
- C风格字符串常量:以
- 符号常量(eg.
const float pi(3.14)
)
- 整数常量
- 运算符
- 算术运算符:
+ - * /
%
++
--
- 关系运算符:
==
!=
>
>=
<
<=
- 逻辑运算符:
&&
(短路特性)||
!
- 位运算符:
&
|
^
<<
>>
~
(二进制补码运算符) - 赋值运算符:
=
及其衍生
- 算术运算符:
//逗号运算符,先计算表达式1,再计算表达式2,结果为表达式2的值
a = 3*5, a*4 //最终结果为60
//sizeof运算符
//Condition ? x : Y 条件运算符
//指针运算符 & *
1.4 输入输出
- 程序建立一个流对象
- 指定这个流对象与某个文件对象建立连接
- 程序操作流对象,通过文件系统产生作用
<iostream>
定义了cin, cout, cerr, clog<fstream>
文件处理
getline(cin,a); //输入整行字符串,第三个参数指定分隔符,默认为换行
1.5 结构控制
//判断
if-else
switch(day){
case 0: cout<<"Sunday"<<endl; break;
//break防止继续执行
...
default:
...
break;
}
//循环
while(){}
do{}while();
for(;;){}
//break, continue, goto
注意:如果要在case里面定义变量,需要用括号 { }
括起来,不然会出错
1.6 字符串
- C 风格字符串
- 常字符指针,const char *p = str ,指向首地址
- string类
string s1; //构造,默认建立一个长度为0的串
string s2="abc";
string s3=s2; //复制构造函数
//字典序比较,下标访问字符
1.7 other
一、一些类型
//类型别名
typedef 已有类型名 新类型名
using 新类型名 已有类型名
//枚举类型
enum 类型名 {变量值列表};
//枚举元素是常量,枚举元素有默认值
//auto,根据初始值自动推断变量类型
decltype(i) j = 2 //表示j以2为初始值,类型与i一致
二、标识符的作用域与可见性
#include<iostream>
using namespace std;
int i;//全局变量,文件作用域
int main(){
i=5;
{
int i;//局部变量,局部作用域({}中),屏蔽外部
i=7;
cout<<"i="<< i <<endl;//输出7
}
cout<<"i="<< i <<endl;//输出5
return 0;
}
关于可见性:
- 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见
- 对于两个嵌套的作用域,如果内层中声明了外层作用域同名的标识符,则外层的那个表示符在内层中不可见
三、变量的生存期与可见性
#include<iostream>
using namespace std;
int i=1;//i为全局变量,具有静态生存期
void other(){
static int a=2;
static int b;
//a,b为静态局部变量,具有全局寿命,局部可见
//只第一次进入函数时被初始化
int c=10;
//c为局部变量,具有动态生存期
//每次进入函数时都初始化
}
二、面向对象
2.1 概述
如何形成类:抽象出同一类事物的共同属性和行为,形成类。
类与对象的关系:类型与实例的关系,犹如模具与铸件之间的关系。
OOP思想:封装、继承、多态
class Clock
{
private: //私有成员
int hour = 0; //类内初始值
public: //共有成员,外部接口
//通过初始化列表构造函数:
// Triangle(int a, int b, int c) : sa(a), sb(b), sc(c) {};
//一般通用的类都会提供一个默认构造函数
//Clock();
void show();
protected: //保护型成员
}
Clock::show(){ //成员函数的实现
}
Clock t; //对象定义
t.show() //调用
如何定义类的成员函数:
(1)标准形式,头文件中声明,cpp文件中实现
(2)隐式内联,直接在头文件中定义且实现
(3)现实内联,头文件中声明内联函数,cpp文件中实现
2.2 构造函数
构造函数的特征:
- 构造函数必须与类重名
- 构造函数可以有任意类型的参数
- 不能有返回值,不能定义返回类型
- 构造函数在 建立对象时自动执行
// complex.h
class Complex{
private:
double real;
double imag;
public:
Complex(double r, double i);
};
//complex.cpp
#include "complex.h"
Complex::Complex(double r, double i){
real = r;
imag = i;
}
一个类中可以没有用户定义的构造函数,此时编译器会自动为用户提供一个 默认构造函数 (无参构造函数)。
- 参数表为空的构造函数
- 全部参数都有默认值的构造函数
但两个默认构造函数在类中同时出现时,会产生编译错误。
class ...{
private:
public:
类名(形参);//构造函数
类名(const 类名 &对象名);//复制构造函数
}
//对象数组的构造
// 1.缺省构造函数,无参数构造函数
classname foo[20];
// 2.构造函数带一个参数,且不是默认参数的情况
classname foo("parm");
// 3.构造函数的参数有多个时,为每个元素显式调用其构造函数
classname foo[2]={
fo(...),
fo(...)
};
//
拷贝构造函数:
- 每个类都有拷贝构造函数,如果没有定义,则使用默认的
- 使用默认的拷贝构造函数,实现的功能也是内存的直接填充
Class Point{
private:
int x,y;
public:
Point(int a, int b): x(a),y(b){}
Point(const Point &p){ //拷贝构造函数,,,构造
x=2*p.x;
y=2*p.y;
}
}
Point p1(30,40);
Point p2(p1); //显式调用拷贝构造函数复制对象
Point p3=p2; //通过赋值来调用拷贝构造函数,隐式调用
Point p4(0,0);
p4=p1; //这里是赋值,不是复制。
Point func2(){
Point p4(10,20);
return p4;
}
p2=func2() //传出来的是对象拷贝构造后的,20,40
下列情况中,不会调用拷贝构造函数的是( B )。
A) 用一个对象去初始化同一类的另一个新对象时
B) 将类的一个对象赋值给该类的另一个对象时
C) 函数的形参是类的对象,调用函数进行形参和实参结合时
D) 函数的返回值是类的对象,函数执行返回调用时
2.3 析构函数
- 一个类中可以有多个构造函数,只能有一个析构函数。
- 如果程序中未声明,编译器自动产生一个默认的析构函数。
- 在对象的生存期结束的时候,系统自动调用析构函数。
- 析构函数没有参数,没有返回类型
class ...{
private:
public:
类名(形参);//构造函数
~类名();//析构函数
}
classname::~classname(){}//析构函数
2.4 静态成员&函数
静态成员
//在Student中 声明 一个静态的学生总数变量
static int stu_cnt;
//构造函数中添加
stu_cnt++;
//在任意一个对象中修改该值,会影响到该类的所有对象
//静态成员在编译时初始化,是一块共享的内存空间
//在类的外部,main的外部,初始化静态成员变量
int Student::stu_cnt=0;
//静态成员函数不能声明为虚函数
//静态数据成员是类的所有对象所共有的
//静态数据成员能受private控制符的作用,可以直接用类名调用
//静态数据成员在类外初始化
静态成员函数
- 静态成员函数仅用来操作静态成员变量
- 静态成员函数是类的“全局”函数,因此没有this指针。在静态成员函数中,一般无法直接访问类的其他变量
- 静态成员函数仅用来操作静态成员变量
- 在任何对象被构造之前,静态成员变量和静态成员函数就已经可以访问。
调用:类名::静态函数名();
static函数 属于类,不属于对象(对象里不分配内存),this指针是对象的指针。所以在cpp静态成员函数里,不能直接使用this指针。
2.5 友元&运算符重载
友元函数,在类的外部,访问类的私有成员
//友元函数在类中声明
friend float dist(Point &a, Point &b);
//友元函数访问私有成员变量,但是不能直接在定义友元函数时调用私有成员变量,只能通过引用类对象的成员参数方式调用
float dist(Point&a, Point &b){
double x = a.x - b.x;
double y = a.y - b.y;
return sqrt(x*x + y*y);
}
//友元函数不是类的成员函数
//现行的C++中,通常用于运算符重载函数。
运算符重载
(1)不能重载的运算符:.
.*
::
sizeof
?:
(2)不是C++的运算符:**
,不可重载
(3)运算符操作目数不变,优先级不变,结合特性不变。
(4)运算符重载,其操作数至少有一个是类的对象。
考察:自增,自减重载
2.6 派生&继承⭐
class car{
//基类中属性与方法
int f(){ k = 0 };
}
class bus: public car{
//出现在基类中的属性与方法,不用再次声明和实现
//只写派生类中需要新添的属性和方法
int f(){ k = 1 }; //派生类中重新定义f()
int g(){car::f();} //调用基类方法
}
(2)访问控制和继承
- 共有继承
- 基类的
public
和protected
成员,访问属性在派生类中保持不变 - 基类的
private
成员,不可以直接访问 - 派生类中的成员函数:可以访问基类中的
public
和protected
成员,不能访问基类中的private
成员 - 通过派生类的对象,只能访问
public
成员
- 基类的
- 私有继承
- 基类的
public
和protected
成员,都以private
身份出现在派生类中 - 基类的
private
成员,不可以直接访问 - 派生类中的成员函数:可以访问基类中的
public
和protected
成员,不能访问基类中的private
成员 - 通过派生类的对象,不能直接访问从基类继承的任何成员
- 基类的
- 保护继承
- 基类的
public
和protected
成员,都以 protected 身份出现在派生类中 - 基类的
private
成员,不可以直接访问 - 派生类中的成员函数:可以访问基类中的
public
和protected
成员,不能访问基类中的private
成员 - 通过派生类的对象,不能直接访问从基类继承的任何成员
- 基类的
protected成员的特点与作用
对建立其所在的类对象的模块来说,用 private 成员的性质;(即类外无法访问)
对其派生类来说,同 public 成员的性质
一个派生类继承基类方法时,以下情况除外
(1)基类的构造函数、析构函数、拷贝构造函数
(2)基类的重载运算符
(3)基类的友元函数
- 派生类调整基类成员的方法
- 同名覆盖
- 访问声明
- 在派生类的public段中声明父亲类中某个函数,令其属性为public
//虚继承
//祖先类的成员只构造一次
class Derived: virtual public Base{...};
(4)派生类的类型转换
class Base{...}
class Derived: public Base{...}
Base b;
Derived d;
b = d; //用派生类对象给基类对象赋值
Base &br = d; //定义一个基类应用,用派生类初始化
Base *bp = &d; //定义一个基类指针,指向派生类对象
(5)派生类的构造
Teacher::Teacher(string n, int a, int s, int i, string sc, string c)
:Person(n, a, s) // 将前3个参数传入继承的Person属性
{
id = i;
sch = sc;
course = c;
}
2.7 多态&虚函数⭐
父亲类定义函数接口,孩子类负责实现该接口。当父亲类需要调用该接口时,能够自动执行对应孩子实现的代码
(1)虚函数
通过虚函数实现运行时多态,虚函数是动态绑定的函数。
//虚函数是属于对象的,不属于类
//一般成员函数、析构函数可以是虚函数,构造函数不能是虚函数
#include<iostream>
using namespace std;
class Base1{
public:
void display() const{
cout << "Base1::display()" << endl;
}
};
class Base2:public Base1{
public:
void display() const{
cout << "Base2::display()" << endl;
}
};
class Derived:public Base2{
public:
void display() const{
cout << "Derived::display()" << endl;
}
};
void fun(Base1 *ptr){
ptr->display();
}
int main(){
Base1 base1;
Base2 base2;
Derived derived;
fun(&base1); //运行时使用指针访问继承中的同名成员会出错
fun(&base2); //全部输出为Base1::display()
fun(&derived); //使用指针,出错
}
//可通过虚函数实现运行时多态,进行改进
virtual void display() const;
(2)虚析构函数
如果允许通过基类指针去调用对象的析构函数(通过delete这样是正常的),就要让基类的析构函数为虚函数。
Base *p = new Derived();
//只在派生类中构造析构时,动态内存
//这样的调用,在析构时内存泄漏
//解决方法:将析构函数变成虚析构(两个)
//根据虚函数派生来的对象同原型函数,可不写virtual
(3)纯虚函数
virtual 函数原型 函数名(参数表) = 0;
抽象类,(带有纯虚函数的类),无法用于定义对象
override
,检查是否覆盖
final
,不允许被继承或覆盖
2.8 other
//类的组合
//在一个类的构造中嵌入另一个类的对象,构造时都要构造
//先构造被组合的
//对象指针
Ccube* pcube; //定义指针时,不会调用构造函数
//定义类时,类的方法编译后的二进制码放入内存
//定义类的对象时,对象的内存空间存放属性数据
//在每个成员函数执行的时候,隐形地传进去一个指向调用者对象自身的 this 指针
a.disp() -> a.disp(this) -> a.disp(&a)
//常对象
const class_name foo();
class_name const foo();
//此后foo对象在其生存周期内不可更改
//const数据成员只能透过初始化列表在构造的时候进行初始化。一旦构造不可更改
//常成员
//一个类中定义的const函数,专门用来处理这个类的const对象发起的函数调用
class Date{
private:
int year, month, day;
public:
Date(int y, int m, int d):year(y), monnth(m), day(d){}
void showdate(){
cout<<y<<endl;
}
void showdate() const{
cout<<"const"<<endl;
}
}
int main(){
Date date1(1990,12,1);
date1.showdate();
Date const date2(1998,1,1);
date2.showdate(); //调用const函数
return 0;
}
//const函数能访问普通属性成员,但不能修改值
三、高级语法
3.1 指针⭐
(1)指针说明
- 有效指针。指向本程序内存范围内有效的地址
- NULL指针。存在但不指向任何地方
- 野指针。指向未知内存的指针,如:
char *p
(2)const 与指针
const char *p = "city"; //指向字符型常量的指针,它指向的值不能修改
char * const p = "city"; //指向字符型变量的常指针,不能指向别的变量,但指向变量的值可以修改
const char const * p = "city"; //指向字符型常量的常指针。
(3)指向对象的指针
Point a(4,5);
Point *p1 = &a;
cout << p1->getX() << endl; //指针访问对象成员
this
指针,隐含于类的每一个非静态成员函数中
3.2 引用
- 引用(&)是标识符的别名
- 定义时需初始化,不能定义空引用
- 区分取址、引用
- 引用不可更改
- 引用必须有类型,且与原类型相同
- 引用的引用仍然是引用
int &j = i;
int *p = &j;
-->int *p = &i;
//引用作为函数参数(声明中),说明参数是对变量的引用
//引用还可以作为函数返回值
3.3 内存操作
动态内存(基础):
int* p; //指针定义
p = new int[10]; //分配空间,大小可以确定、也可以不确定
delete [] p; //释放空间
p = NULL
二维动态数组:
char** pchar = new char*[m];
for(int i = 0; i<m; i++)
pchar[i] = new char[n];
for(int i = 0; i<m; i++) //释放空间
delete [] pchar[i]; //不然会导致内存泄漏
delete [] pchar;
3.4 函数
在C++中,不再单纯依靠函数名字来确定函数,因此对函数的声明要求更加严格。
以下为合法的函数声明:
int Area(int x, int y);
int Area(int, int);
(2)内联函数
//内联函数
//减少开销,不会为函数单独开辟区域编译
//不能有循环语句和switch语句
//对内联函数不能进行异常接口声明
//编译器可能优化,,导致用得比较少
inline int max(int a, int b){
return a>=b ? a : b;
}
(3)默认参数
//默认参数,也叫缺省参数
//默认参数必须放在没有默认值参数的后面
int average(int x, int y, float alpha = 1.0)
(4)函数重载
//函数重载
//允许多个函数使用同样的函数名
//不要将功能不同的函数声明为重载函数
bool show(int a, char *p); //原始函数
bool show(float a, char *p); //参数类型不同
bool show(int a, char *p, float c); //参数数量不同
int add(int x, int y);
float add(float x, float y);
//编译器不以形参名区分
//编译器不以返回值来区分
3.5 结构体
struct name{
共有成员
protected:
保护型成员
private:
私有成员
}
//cpp中结构体是一个特殊的类
//结构体中可以有数据成员和函数成员
//一定情况下可如下初始化:
Student stu={201911,"yasuo",19};
3.6 函数模板
提高代码的复用性
template<typename T>
T abs(T x) {
return x < 0? -x : x;
}
//自动推导其变量类型
3.7 other
extern
用于全局变量、函数
#if #elif #else #endif
条件编译语句
C++ 文件和流,菜鸟教程
C++ 异常处理,菜鸟教程
四、实例
4.1 三角形类
#include<iostream>
#include<string>
#include<cmath>
using namespace std;
class Triangle {
private:
int sa, sb, sc;
public:
// Triangle(int a, int b, int c) : sa(a), sb(b), sc(c) {}; 通过初始化列表构造函数
Triangle(int a, int b, int c) { //普通构造函数
sa = a;
sb = b;
sc = c;
}
bool ifcan() {
if (!((sa < 0 || sb < 0 || sc < 0) || (sa + sb < sc) || (sa + sc < sb) || (sb + sc < sa)))
return true;
else
return false;
}
double GetPerimeter(){
return sa + sb + sc;
}
double GetArea(){
double p = GetPerimeter() / 2;
p = p *(p - sa) * (p - sb) * (p - sc);
return sqrt(p);
}
string classify() {
int flag;
if (sc >= sa && sc >= sb)
flag = sc*sc - sa*sa - sb*sb;
else if (sa >= sb && sa >= sc)
flag = sa*sa - sc*sc - sb*sb;
else
flag = sb*sb - sa*sa - sc*sc;
if (!flag)
return "right triangle";
else if (flag > 0)
return "obtuse triangle";
else
return "acute triangle";
}
~Triangle(); //析构函数声明
};
Triangle::~Triangle(void) {
cout << "triangle is being deleted" << endl;
}
int main() {
int a, b, c;
cout << "input 3 numbers:" << endl;
cin >> a >> b >> c;
Triangle t(a,b,c);
if (t.ifcan())
cout << "can form a triangle." << endl;
else
cout << "can't form a triangle." << endl;
cout << "perimeter: " << t.GetPerimeter() << endl;
cout << "area: " << t.GetArea() << endl;
cout << "category: "<< t.classify() << endl;
system("pause");
return 0;
}
4.2 两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> hashtable;
for(int i = 0; i<nums.size(); i++){
auto it = hashtable.find(target - nums[i]);
if (it != hashtable.end()){
return {it->second, i};
}
hashtable[nums[i]] = i;
}
return {};
}
};
other
参考资料:
- 学校课程 30%
- C++语言程序设计(学堂在线) 20%
- C++ Primer 5th
- 菜鸟教程
相关文章:
关于考试:
- 类写完,要加括号
- 多态的含义
- 设计模式,3抽1