基础C++知识
1. 基本模板
#include <iostream>
using namespace std;
int main()
{
// 输出
cout << "hello naka " << endl;
system("pause");
return 0;
}
2. 注释
3. 变量 常量
- 变量 为一个一定大小的空间取一个名字,方便之后使用
- 常量,定义初始化之后不允许修改
#define
、const
4. 关键字
- 修饰符类型:
- 用户标识符区分大小写、见名知意
5. 数据类型
6. 运算符
7. 程序流程结构
- 顺序、选择、循环
- if 、switch 、? : 、 for 、while 、do while
8. 数组
- 数组名,数组名指示该数组在内存中的首地址,普通变量取址符
&
- 二维数组定义时,最少给出
int a[][2]
= {{1},{1,2}}
9. 函数
- 函数的定义、调用、值传递、声明(被调用的函数写在前面)
- 函数的分文件编写
- 创建
.h
后缀的头文件 - 创建
.cpp
后缀的源文件 - 在头文件中写函数的声明
- 在源文件中写函数的定义
- 在使用函数的文件中引入该头文件
#include "我的头文件.h"
- 创建
- 函数的默认参数
void func(int a, int b= 10){}
- 有默认值的参数必须放在参数列表的后面
-
函数重载
在同一个作用域下;函数名称相同;函数参数类型不同 或者个数不同或 顺序不同
-
10. 指针
定义,使用
int a, *p;
p = &a;
*p = 1000;
// 空指针,用来初始化指针,不能进行访问
p = NULL;
常量指针,用const修饰指针
const int * p
,指针的指向可以修改,指针所指向的值不可修改- const int * p
- 指针常量,
int * const p
,指针的指向不可以改,指针指向的值可以改- int * const p
- 指针和函数(参数)
- 值传递
- 地址传递
11. 结构体
- 定义,使用 ```cpp struct Student { string name; int age; int score; };
struct Student s; // 这里的struct可以省略 s.name = “Naka”;
struct Student s1 = {“Naka”, 41, 98};
2. 结构体数组
2. 结构体指针,使用`->`访问成员
2. 结构体或结构体指针做函数参数
2. 使用const修饰结构体变量的定义,防止误修改
<a name="qS4W5"></a>
## new操作符
1. new的基本语法
1. new创建的变量会被存放在堆区,由程序员管理创建与释放
1. new返回的是该数据类型的指针
1. 使用`delete`关键字释放
```cpp
int *p = new int(10);
delete p;
- 使用new创建数组
int *p = new int[10];
- 释放数组
delete [] p;
- 释放数组
引用
- 数据类型 &变量2 = 变量1;
- 两个变量操作的是同一个空间
- 引用必须要初始化,指明对谁的引用
- 引用一旦初始化后,就不可更改引用的指向
- 在函数传参时,可以用引用作为形参,这样也可以通过形参来修改实参
引用可以作为函数的返回值
int& test(){
static int a = 10;
return a; // 返回了a的引用
}
...
int &b = test();
cout << b << endl; // 10
test() = 1000; // 函数的调用作为等式的左值
cout << b << endl; // 1000
引用的原理也是指针常量
类和对象
class Circle { private: double c_r = 10; string c_name;
public: void setName(string name) { c_name = name; } string getName() { return c_name; } void setR(double r) { c_r = r; } double zhouchang() { return 2 PI c_r; } };
int main() { // 实例化 Circle c; c.setName(“圆”); c.setR(11.2); cout << “周长:” << c.zhouchang() << endl;
system("pause");
return 0;
}
3. 访问权限:
1. 公共`public`
1. 保护`protected`,类外不可访问,继承类可以访问
1. 私有`private`,类外不可访问,继承类不可以访问
1. 默认访问权限为私有
<a name="yU6r2"></a>
## 构造函数和析构函数
1. 编译器自动提供(空实现的),也可以自己写
<a name="O6MKt"></a>
### 构造函数
1. 在创建对象时为对象的成员属性赋值
1. `类名(){ }` 没有返回值也不写void,名字与类名相同
1. 可以有参数,发生重载
2. 两种分类方法:
1. 按参数分 有参、无参
1. 按类型分 普通、拷贝
3. 三种调用方法:
1. 括号法
1. `Person p1;` 调用默认无参构造函数 注意不加`()`
1. `Person p2(10);` 调用有参普通构造函数 将属性值传入
1. `Person p3(p2);` 调用有参拷贝构造函数
2. 显示法
1. `Person p1;` 调用默认无参构造函数 注意不加`()`
1. `Person p2 = Person(10);` 调用有参普通构造函数 将属性值传入
1. `Person p3 = Person(p2);` 调用有参拷贝构造函数
3. 隐式转换法
1. `Person p1 = 10;` 相当于`Person p1 = Person(10);`
1. `Person p2 = p1;` 相当于`Person p2 = Person(p1);`
4. 拷贝构造函数的写法
```cpp
class Person{
string name;
Person(const Person &p){
name = p.name; // 调用此构造函数的对象会被赋予同p相同的属性值
}
}
- 拷贝构造函数的调用时机
- 用一个已有的对象来创建一个新对象
- 值传递的方式给函数参数传值
- 值方式返回局部对象
析构函数
- 对象销毁前系统自动调用,执行一些清理工作
~类名(){ }
没有返回值也不写void,名字与类名相同,在名称前加~- 不可以有参数
深拷贝与浅拷贝
- 浅拷贝:简单的复制拷贝操作,
int a, b; a = 10; b = a;
- 深拷贝:在堆区重新申请空间,进行拷贝操作,
int a = 10; int *b = new int(a);
构造函数初始化列表
- 初始化各个属性值
类名(参数1, 参数2): 属性1(参数1), 属性2(参数2) { }
类对象作为类的成员
静态成员
静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
-
静态成员函数
定义,在函数的返回类型前加
static
,static coid func(){ }
- 访问方式
对象名.方法名();
类名::方法名();
- 静态方法只能访问静态属性,不能访问其他属性
- 静态成员函数也具有访问权限
所有对象共享同一个静态成员函数
空对象占一个字节(没有一个成员的类对象)
- 静态成员不属于某一个对象,所以静态成员单独存放,不占对象的内存空间
- 成员函数和成员属性也单独存放,只有非静态成员变量属于类的对象上 才占对象的内存空间
this指针
- this指针指向被调用的成员函数所属的对象,用来区分调用该成员函数的对象时哪一个
- this指针隐含在每一个非静态成员函数内,直接使用
- 当形参和成员名相同时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身时,可使用
return *this;
```cpp class Person{ public: int age; Person(int age) {
} // 返回引用 !! Person& add10(){this->age = age;
} }this->age += 10;
return *this;
// 调用 Person p(10); p.add10().add10();
1. 空指针(`Person *p = NULL;`)可以访问成员函数,需要注意若函数内使用了成员变量(隐含有this指针) 则会出错,否则可以正常访问。
<a name="Xnzqu"></a>
### const修饰成员函数
**常函数**
- 成员函数后加const后称为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
```cpp
class Person{
public:
int age;
Person(int age) {
this->age = age;
}
void func() const {
age = 10; // 出错
}
}
- this指针的本质是一个指针常量
Person * const this;
其指向不可以修改 - 在成员函数后加上const后,就把this指针变成了常量指针
const Person * const this;
其指向的值也不可以修改
常对象
- 声明对象前加const称为常对象
const Person p;
- 常对象只能调用常函数,因为普通成员函数可以修改成员属性
- 其属性不允许被修改,否则在成员属性的定义前加mutable
友元
全局函数做友元
- 让一个全局函数可以访问一个类的私有成员
```cpp
class Person {
friend void personFriend(Person* p); // 声明该全局函数为友元
private:
int age;
public:
Person(int age) {
} };this->age = age;
// 全局函数做友元 void personFriend(Person* p) { cout << “全局函数做友元 访问私有属性值:” << p->age << endl; }
void test() { Person p(40); personFriend(&p); }
<a name="uQDzl"></a>
### 类做友元
- 让一个类可以访问另一个类的私有成员
```cpp
class Person {
friend class Chinese; // 声明为友元
private:
int age;
public:
Person(int age) {
this->age = age;
}
};
// 类做友元
class Chinese {
public:
Person* p;
Chinese() {
p = new Person(30);
}
void visit() {
cout << "类做友元 访问私有属性 " << p->age << endl;
}
};
void test() {
Chinese c;
c.visit();
}
成员函数做友元
- 让一个类的部分成员函数可以访问另一个类的私有成员
- 前向声明,告诉编译器有这么一个类,但编译器不知道其大小 也不能访问。声明、预定使用、定义、使用 ```cpp class Person; // 前向声明该类,但具体使用还是只能在类定义之后
// 成员函数做友元 class Japanese { private: Person* p; // 预定使用 public: int m_age; Japanese(int age); // 在类外实现 void visit(); }; class Person { friend void Japanese::visit(); // 声明为友元 private: int age; public: Person(int age) { this->age = age; } }; // 在类外实现成员函数,因为要在前向声明的类定义之后 Japanese::Japanese(int age) { m_age = age; p = new Person(age); } void Japanese::visit() { cout << “成员函数做友元 访问私有属性 “ << p->age << endl; } void test() { Japanese j(20); j.visit(); }
<a name="NruPE"></a>
## 运算符重载
**概念**:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型<br />要用了再学
<a name="m6lgo"></a>
## 继承
**优点**:代码复用<br />**语法** 定义子类时,`class 子类: 继承方式 父类{ };`
- 子类 - 派生类
- 父类 - 基类
- 构造与析构的顺序,创建子类对象时
- 父类构造 -> 子类构造 -> 子类析构 -> 父类析构
- 当子类和父类的成员属性`m_A`同名时,使用子类对象`Son s;`直接访问`s.m_A;`的是子类成员属性,外加父类作用域`s.Base::m_A`才能访问父类成员属性,访问父类的同名成员函数`s.Base::func();`
<a name="vIswq"></a>
### 继承方式
1. 公共继承 `public`
1. 保护继承 `protected`
1. 私有继承 `private`
| 父类 | 子类1(公共继承) | 子类2(保护继承) | 子类3(私有继承) |
| --- | --- | --- | --- |
| 公共权限成员 | 公共权限 | 保护权限 | 私有权限 |
| 保护权限成员 | 保护权限 | | |
| 私有权限成员 | 不可访问 | | |
4. 父类的私有成员在子类中即使不能被访问,但还是被继承了。
<a name="LGAhj"></a>
### 多继承
**语法**:`class 子类: 继承方式 父类1, 继承方式 父类2{ };`
1. 当多个父类中存在同名的成员时,子类访问时需要加作用域区分
1. 菱形继承时,可能存在数据浪费(两个父类存在同名的数据,子类无法确定是使用父类1的数据还是父类2的数据)
1. 解决,使用**虚继承**,在继承之前加关键字`virtual`
```cpp
class Animal{
public:
int m_age;
}
class Sheep: virtual public Animal{ };
class Tuo: virtual public Animal{ };
class SheepTuo: public Sheep, public Tuo{ };
SheepTuo st;
st.Sheep::m_age = 100;
st.Tuo::m_age = 200;
cout << st.m_age << endl; // 200
多态
两类:
- 静态多态,函数重载、运算符重载 函数地址早绑定 - 编译阶段确定函数地址
- 动态多态,派生类、虚函数实现运行时多态 函数地址晚绑定 - 运行阶段确定函数地址
class HomePage {
public:
// 虚函数
virtual void contain() {
cout << "主页面中间" << endl;
}
};
class MyPage: public HomePage {
public:
// 重写父类的虚函数
void contain() {
cout << "我的页面中间" << endl;
}
};
void doDraw(HomePage &hp) { // 子类可以自动转化为父类
hp.contain();
}
void test(){
MyPage mp;
doDraw(mp); // 没加virtual 主页面,加了virtual 我的页面,根据传入的对象调用
}
动态多态实现条件
- 有继承关系
- 子类重写父类中的虚函数
多态使用:父类指针或引用指向子类对象