变量
基本数据类型
类型 | 说明 |
---|---|
bool | 布尔型 |
char | 字符 |
int | 整形 |
float | 浮点型 |
double | 双精度浮点 |
void | 无类型 |
wchar_t | 宽字符型,定义:typedef short int wchar_t; |
修饰符 | 说明 |
---|---|
signed | 有符号 |
unsigned | 无符号 |
short | 短型,size缩一半 |
long | 长型,size扩一倍 |
直接上代码: |
void testVariant()
{
int p1 = 1;
long p2 = 2L;
float p3 = 3.0;
double p4 = 4.0;
char p5 = 'a';
bool p6 = false;
short p7 = 100;
printf("value = %d, sizeof p1 = %d\n", p1, sizeof(p1)); //value = 1, sizeof p1 = 4
printf("value = %ld, sizeof p2 = %d\n", p2, sizeof(p2)); //value = 2, sizeof p2 = 4
printf("value = %f, sizeof p3 = %d\n", p3, sizeof(p3)); //value = 3.000000, sizeof p3 = 4
printf("value = %lf, sizeof p4 = %d\n", p4, sizeof(p4)); //value = 4.000000, sizeof p4 = 8
printf("value = %c, sizeof p5 = %d\n", p5, sizeof(p5)); //value = a, sizeof p5 = 1
printf("value = %b, sizeof p6 = %d\n", p6, sizeof(p6)); //value = b, sizeof p6 = 0
printf("value = %d, sizeof p7 = %d\n", p7, sizeof(p7)); //value = 100, sizeof p7 = 2
}
指针与引用
指针部分与C相同,C++相比C多了两个概念:对象指针和引用,对象指针这部分内容在类的定义和使用
中会进行介绍,本节介绍下引用。
引用也是一个地址,记住它的三个特点:
- 与指针一样,需要同类型才可以赋值;
- 除非做函数的返回值或形参,其余定义引用类型的同时就要初始化;
- 引用类型并不是建立一个新的对象,因此不会调用构造函数
引用的使用场景:
- 作为函数的形参或返回值,减少不必要的对象的创建,不用传指针也可以操作对象本身;
示例:
Student stu;
Student &stu_q = stu; //没有创建新的对象
Student stu2 = stu; //看似赋值,实际上创建了一个新的对象
Student *stu_p = &stu; //普通的一级指针
stu_q.study();
stu_p->study();
// stu、stu_q、stu_p是同一个对象,而stu2是新的对象
printf("stu = %#x\nstu_q = %#x\nstu_p = %#x\nstu2 = %#x\n",&stu,&stu_q,stu_p,&stu2);
static和const的作用
- static:
C/C++中的作用于Java中的非常类似,不同点是static的变量需要在类的外部初始化; - const:
相当于Java中的final,具有两大特性:1)不可修改值;2)定义时初始化(或在构造函数中); - static+const
类的静态常量可以在类内部初始化;
#include <iostream>
using namespace std;
class A{
public:
//可以直接初始化
const int a = 0;
const int b;
const int c;
static int d;
//静态常量可以在类内初始化
static const int e = 4;
//在构造方法初始化
A(int b,int c):b(b),c(c){
}
void print(){
cout<<a<<"/"<<b<<"/"<<c<<"/"<<d<<"/"<<e<<"/"<<endl;
}
};
//静态变量再类外初始化
int A::d =3;
int main(int argc, const char * argv[]) {
A a(1,2);
a.print();
return 0;
}
- const对象
#include <iostream>
using namespace std;
class A{
public:
const int a;
int b;
A(int a):a(a){
b=2;
}
void print(){
}
void print1() const{
cout<<a<<"/"<<b<<endl;
}
};
int main(int argc, const char * argv[]) {
const A a(1);
const A a1(2);
//编译失败,常对象不能被赋值
// a=a1;
//此处编译失败,常对象不能调用非常成员函数
// a.print();
a.print1();
return 0;
}
- const成员函数
- 常成员函数的定义和声明都需要包含const
- 长城园函数只能调用常成员函数,而不能调用非常成员函数,可以访问但不可以更改非常成员变量
#include <iostream>
using namespace std;
class A{
public:
const int a;
int b;
A(int a):a(a){
b=2;
}
void print(){
}
void print1() const{
//此处编译错误,常成员函数不可以改变非常成员变量
// b=3;
//此处编译错误,常成员函数不可以调用非常成员变量
// print();
cout<<a<<"/"<<b<<endl;
}
};
int main(int argc, const char * argv[]) {
const A a(1);
const A a1(2);
//编译失败,常对象不能被赋值
// a=a1;
//此处编译失败,常对象不能调用非常成员函数
// a.print();
a.print1();
return 0;
}
类的定义和使用
1. public\protected\private访问修饰符
修饰符 | 含义 |
---|---|
private | 表示私有,他所修饰的成员,只能在类的内部访问,外界不能访问 |
protected | 除了类内部可以访问,他的子类也可以访问 |
public | 内部外部都可以访问 |
2. 类的定义
第一种定义形式:
class Student{
public:
char name[100];
int socre;
int print(){
cout<<"/"<<name<<"/"<<socre<<endl;
return 0;
}
}; //注意一定要以“;”结尾
第二种形式:
class Student{
public:
char name[100];
int socre;
int print();
};
//1. 成员函数仅仅在类内声明函数原型,在类外定义函数体
//2. 在类外定义函数体的需要类名加上::域限定符
int Sts::print(){
cout<<"/"<<name<<"/"<<socre<<endl;
return 0;
}
3. 类的使用
- 普通创建:
Student st; //定义即可使用,不用初始化
Student *p_st = &st;
st.socre=100;
strcpy(st.name, "wang");
st.print();
p_st->print();
- 使用new创建:
int *num= new int; //new只能创建指针变量
delete a; //new出来的变量必须调用delete释放内容
int* arr=new int[10];
delete []arr;
Student *p_st = new Student();
delete p_st;
4. 类的构造函数、析构函数、拷贝构造函数(浅拷贝与深拷贝)
这两个概念是成对的,构造=创建+初始化,析构=销毁+释放资源,拷贝构造=创建+内容复制理解这个概念是关键。
- 如果用户不定义析构函数,则系统会自动生成一个,如果用户定义,则会在对象销毁时自动调用;
- 构造函数可以重载,但是析构函数不可以重载,但他可以是虚函数,一个类只能有一个析构函数;
- 与类同名,且形参是本类对象的引用类型函数,叫做拷贝构造函数,与构造函数一样,如果我们不主动定义,那么系统会自动生成一个(浅拷贝),进行俩个对象成员之间的赋值,用来初始化一个对象
构造函数示例:
#include <iostream>
#include <cstring>
using namespace std;
class Student {
public:
char name[100];
int socre;
// 定义构造函数,实现在外面
Student (char *str,int sor);
// 定义成员函数,实现在外面
void print();
};
Student::Student(char *str,int sor){
strcpy(name, str);
socre=sor;
cout<<"构造函数"<<endl;
}
void Student::print(){
cout<<a<<"/"<<name<<"/"<<socre<<endl;
}
int main(int argc, const char * argv[]) {
Student stu(100,"heihei",200);
stu.print();
return 0;
}
析构函数示例:
#include <iostream>
#include <cstring>
using namespace std;
class Status{
public:
int a ;
char name[100];
int sorce;
Status(int a ,char *str,int sor);
~Status();
};
Status::Status(int a,char *str,int sor){
this->a=a;
strcpy(name, str);
sorce=sor;
cout<<this->a<<name<<sorce<<endl;
}
Status::~Status(){
cout<<"析构函数"<<endl;
}
int main(int argc, const char * argv[]) {
Status sta(1,"aaa",200);
Status stw(10,"aaaq",2001);
return 0;
}
输出:
1aaa200
10aaaq2001
析构函数
析构函数
拷贝构造函数示例:
#include <iostream>
#define AA 3.999
using namespace std;
class Status{
public:
int a ;
char *str;
Status(int a,char* b);
Status(Status &A);
~Status();
};
Status::Status(int a,char* b){
this->a=a;
this->b=new char[strlen(b)+1];
strcpy(this->b,b);
}
// 一般定义拷贝函数的目前是为了做深拷贝,因为系统默认的拷贝函数是浅拷贝
Status::Status(Status &A){
this->a=A.a;
this->b=new char[strlen(b)+1];
strcpy(this->b,b);
}
Status::~Status(){
delete []b;
}
int main(int argc, const char * argv[]) {
Status st(1,2);
Status sts(st);
return 0;
}
5. friend友元类、函数
要点:
- friend函数用于实现外部函数访问本类的私有属性或方法;
- friend类用于实现外部类访问本类的私有属性和方法;
- 友元函数
#include <iostream>
using namespace std;
class Status{
private:
int a;
int b;
public:
Status(int a,int b){
this->a=a;
this->b=b;
};
// friend函数需要在本类进行定义
friend void print(Status st);
};
void print(Status st){
cout<<st.a<<"/"<<st.b<<endl;
}
int main(int argc, const char * argv[]) {
Status st(1,2);
print(st);
return 0;
}
- 有元类
#include <iostream>
using namespace std;
class Status{
private:
int a;
int b;
public:
Status(int a,int b){
this->a=a;
this->b=b;
};
friend class A;
};
class A{
public:
void print(Status &st){
cout<<st.a<<"/"<<st.b<<endl;
}
};
int main(int argc, const char * argv[]) {
Status st(1,2);
A a;
a.print(st);
return 0;
}
6. 类的继承(派生)
与Java差不多,直接看demo:
#include <iostream>
using namespace std;
class A{
public:
int a;
A(){
cout<<"父类的无参构造函数"<<endl;
}
A(int a){
this->a=a;
cout<<"父类的有参构造函数"<<endl;
}
~A(){
cout<<"父类的析构函数"<<endl;
}
void setA(int a){
this->a=a;
}
void showA(){
cout<<a<<endl;
}
};
// 三种继承方式:public、protected、private,决定子类继承来的属性和方法的访问权限
class B:public A{
public:
int b;
//这里B的构造函数虽然没有显式调用A的构造函数,但是会先执行A的构造函数
B(){
cout<<"子类的无参构造"<<endl;
}
//指定父类带参构造和参数
B(int a):A(a){
cout<<"子类的有参构造函数"<<endl;
}
// 会先调用子类B的析构函数,在调用父类A的析构函数
~B(){
cout<<"子类的析构函数"<<endl;
}
void setB(int b){
this->b=b;
}
void showB(){
cout<<b<<endl;
}
};
int main(int argc, const char * argv[]) {
B b;
b.setA(1);
b.setB(2);
b.showA();
b.showB();
return 0;
}
7. 类的多态(virtual虚基类、虚函数、抽象类)
virtual在功能上可以类比java的abstract关键词,
如上图的继承关系,无法通过编译(多次拷贝有歧义),此时的解决办法就是在B、C的定义上加上virtual关键词。
上面是虚基类的应用,除此之外,虚函数可以实现多态:
#include <iostream>
using namespace std;
class A{
public:
// 在父类中定义虚函数或虚析构函数
virtual void print(){
cout<<"A"<<endl;
};
// 1. 纯虚函数没有方法体,由子类实现
// 2. 后面加一个=0,表示没有函数体
// 3. 含有纯虚函数的类就是抽象类,一个抽象类至少有一个纯虚函数
virtual void test()=0;
};
class B : public A{
public:
void print(){
cout <<"B"<<endl;
}
void test(){
cout <<"test"<<endl;
}
};
int main(int argc, const char * argv[]) {
A a;
a.print();
B b;
b.print();
A *p;
p=&b;
p->print();
A &pp=b;
pp.print();
return 0;
}
虚函数注意点:
- 虚函数不能是静态成员函数,或友元函数,因为他们不属于某个对象
- 内联函数不能运行中动态指定其位置,即使虚函数在类内定义,编译时,仍将看做非内联
- 构造函数不能是虚函数,析构函数可以是虚函数,而且通常声明为虚函数(这个还是好理解的,不同的子类占用的资源类型和数量不同,利用虚析构函数可以实现不同的资源释放逻辑)
函数
函数特性
1. 导包
导标准库
#include<iostream>
导入三方包
#include "mylib.h"
再来回顾下C语言的导包:
导标准库:
eg. #include <stdlib.h>
导第三方包:
eg. #include “jni.h”
eg. #include “test.c”
c与c++头文件的区别点:
- c++导系统包时<>内没有
.h
- c++中可以用static和inline解决函数链接错误,static告诉编译器对应的函数仅在本c++文件中使用不对外提供(类似Java中的private),inline告诉编译器每次外部调用本函数时都拷贝完整函数代码到调用处
- c++的
.h
采用#pragma once
来解决循环引用问题
标准.h
头文件格式:
#ifndef <自定义标识>
#define <自定义标识>
#ifdef __cplusplus
extern "C" {
#endif
// 正常的申明区
#ifdef __cplusplus
}
#endif
#endif
2. 命名空间
使用了命名空间,可以省去重复的代码,也可以解决命名空间重复的冲突
#include <iostream>
using namespace std;
// main() 是程序开始执行的入口
int main()
{
cout << "Hello World"; // 输出 Hello World
return 0;
}
上述代码不使用命名空间:
#include<iostream>
int main()
{
std::cout<<"Nice to meet you!"<<std::endl;
return 0;
}
也可以指定使用哪些函数的命名空间,不过不常用:
#include<iostream>
using std::cout;
using std::endl;
int main()
{
cout<<"Nice to meet you!"<<endl;
return 0;
}
3. 形参默认值
c++支持函数形参带默认值:
int add(int x=1, int y=2)
{
return x + y;
}
int main()
{
int a,b,c,d;
a = add();
b = add(5);
c = add(10, 12);
// d = add(., 23); //非常遗憾不支持省略左边的参数
printf("a = %d,\nb = %d,\nc = %d\n", a, b, c);
return 0;
}
输出:
a = 3,
b = 7,
c = 22
4. 函数模版template
格式:template<class T1,class T2…>
作用于java的泛型有点类似,上述代码定义了两个类型参数T1和T2,就可以用这两个类型参数作为形参去定义函数,从而实现函数的重载
示例:
#include <iostream>
using namespace std;
template<class T1,class T2>
T1 add(T1 x,T2 y){
cout<<sizeof(T1)<<"/"<<sizeof(T2)<<endl;
return x+y;
}
int main(int argc, const char * argv[]) {
cout<<add(1,2)<<endl;
cout<<add(0.22, 0.33)<<endl;
cout<<add('A',2)<<endl;
return 0;
}
输出:
4/4
3
8/8
0.55
1/4
C
5. 内联函数inline
使用内敛函数编译的时候,把函数代码插入到函数调用的地方, 就像普通的程序执行代码一样。
内敛函数使用场景:函数本身不长,但是又是频繁调用
kotlin也借鉴了这个特性,甚至名字都一样也是inline
示例:
#include <iostream>
using namespace std;
// 1. 内联函数的调用要出现在调用之前,才可以让编译器了解上下文进行代码替换
inline int max(int x,int y){
return x>y?x:y;
}
int main(int argc, const char * argv[]) {
cout<<max(12, 2)<<endl;
cout<<max(2, 13)<<endl;
return 0;
}
6. 异常处理
C++的异常处理和java的差不多都是用到了try,catch,throw三个关键字。
示例:
#include <iostream>
using namespace std;
int main(int argc, const char * argv[]) {
int a =0;
int b=1;
cout<<a<<"/"<<b<<endl;;
try {
if (a==0) {
throw "a为0异常";
}
} catch (const char *str) {
cout<<str<<endl;
}
return 0;
}
C++中标准异常类:
示例:
异常 | 定义 | 场景 |
---|---|---|
bad_alloc | 当new一个对象,内部不足,会抛出bad_alloc异常 | |
out_of_range | 使用string类下标越界时 |
#include <iostream>
#include <new>
#include <stdexcept>
using namespace std;
int main(int argc, const char * argv[]) {
string *s;
try {
s = new string("123456");
cout<<s->substr(7,3);
} catch (bad_alloc &t) {
cout<<"异常:"<<t.what()<<endl;
}catch(out_of_range &t){
cout<<"异常:"<<t.what()<<endl;
}
return 0;
}
输出:
异常:basic_string
输入输出函数
1. 屏幕输出
using namespace std
// 使用<<符号,有点类似于bat脚本语言中重定向符`<`和`>`,只不过这里是两个
cout << "hello c++";
// 输出hello cppfred,无空格无换行
cout << "hello cpp" << "fred";
// 使用endl进行换行
cout <<"hello"<<endl<<"cpp";
2. 键盘输入
// 第一步:定义一个变量接收输入的数据
int a,b;
cout<<"请输入一个数字:"<<endl
// 第二步:使用cin进行赋值
cin>>a;
// cin>>a>>b; //也可以一次赋值给多个变量
// 第三步:打印a的值
cout<<"a=%d" a<<endl
文件读写
C++定义了三个类分别负责,读,写,读写操作:
类型 | 作用 |
---|---|
ofstream (out) | 表示输出文件流,用于创建文件并向文件写入信息。 |
ifstream (in) | 表示输入文件流,用于从文件读取信息。 |
fstream (file) | 表示文件流,且同时具有 ofstream 和 ifstream 两种功能,这意味着它可以创建文件,向文件写入信息,从文件读取信息。 |
打开文件:
void open(const char *filename, ios::openmode mode);
- 第一个参数表示打开文件的路径
- 第二参数表示打开的模式
参数|作用
—|—
ios::in|为输入(读)而打开文件
ios::out|为输出(写)而打开文件
ios::ate|初始位置:文件尾
ios::app|所有输出附加在文件末尾
ios::trunc|如果文件已存在则先删除该文件
ios::binary|二进制方式
除此之外还可以通过|
符号将多个参数进行使用:
ofstream out;
out.open("dotcpp.txt", ios::out|ios::binary) //以二进制模式打开,进行写数据
读写测试:
void testRead(){
char data[100];
ifstream in;
in.open("/Users/renxiaohui/Desktop/test.txt");
in>>data;
cout<<data<<endl;
in.close();
}
void testWrite(){
const char *name="测试测试测试";
ofstream out;
out.open("/Users/renxiaohui/Desktop/test1.txt");
out<<name;
out.close();
}
字符串自操作
// 字符串长度
const char *c_data="abc"
int len_data = strlen(c_data);
// 字符串拷贝
char cc_data[len_data];
strcpy(cc_data, c_data);