C++基础知识

C++基础知识

Hello World

从hello wrld开始

需要导入iostream这个头文件

c++中打印使用cout关键字。不过使用的时候需要加上命名空间std。
每次都加std太麻烦,可以在外面声明一个全局的using namespace std

命名空间相当于java中的包,主要为了防止重名

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <iostream>

using namespace std;

void main() {
//没声明之前
//std::cout << "hello world" << std::endl;
//声明之后
cout << "hello world" << endl;
system("pause");
}

自定义命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
namespace NSP_A {
int a = 1;
struct student {
int age;
char *name;
};
}

namespace NSP_B {
int a = 2;
//命名空间嵌套
namespace NSP_C {
int c = 90;
}
}

void main() {
cout << NSP_A::a << endl;
cout << NSP_B::a << endl;
cout << NSP_B::NSP_C::c << endl;
//使用命名空间中的结构体
using NSP_A::student;
student t;
t.age = 20;
cout << t.age << endl;
system("pause");
}

C中没有布尔类型,C++中有了布尔类型,使用bool修饰。bool类型在内存中占一个字节。

C++中也有跟java一样的三目运算,不过C++中的三目运算运算完之后可以直接赋值,java中不可以。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
bool success = true;

if (success) {
cout << "成功" << endl;
cout << sizeof(bool) << endl;
}
else {
cout << "失败" << endl;
}
//三目运算,最后可以直接复制
int a = 10, b = 20;
((a > b) ? a : b) = 30;
cout << b << endl;

system("pause");
}

C++中的引用。

使用&符号修饰。

1
2
3
4
5
6
int main() {
int a = 20;
int &b = a;
cout << b << endl;
system("pause");
}

上面的代码中,b是a的引用。相当于a的一个别名。它跟指针不同,指针是指向变量的地址,引用只是变量的一个别名。

引用和指针的比较:

  1. 引用能干的事情,指针都能干,不过引用写起来别指针方便。引用可以直接操作变量,而指针需要在前面加个*
  2. 引用必须要有值不能为空。比如我们向一个函数传递一个引用参数,如果为空的话,编译期间就会报错,而如果是传递指针,指针可以为空,编译期间不报错,运行时就会报错了。这样来看使用引用更好。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//指针值交换
void swap_1(int *a, int *b) {
int c = 0;
c = *a;
*a = *b;
*b = c;
}
//引用值交换
void swap_2(int &a, int &b) {
int c = 0;
c = a;
a = b;
b = c;
}
void main() {
int x = 10;
int y = 20;

printf("%d,%d\n", x, y);
//swap_1(&x, &y);
swap_2(x, y);
printf("%d,%d\n", x, y);

system("pause");
}

引用主要用到的地方

  1. 作为函数的参数或者返回值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    struct Teacher{
    char* name;
    int age;
    };
    //引用作为参数
    void myprint(Teacher &t){
    cout << t.name << "," << t.age << endl;
    t.age = 21;
    }
    //指针作为参数
    void myprint2(Teacher *t){
    //(*t).name
    }
    void main(){
    Teacher t;
    t.name = "Jason";
    t.age = 20;
    myprint(t);
    myprint2(&t);
    system("pause");
    }
  2. 拿指针的引用来代替二级指针

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    struct Teacher {
    char* name;
    int age;
    };
    //使用二级指针
    void getTeacher(Teacher **p) {
    Teacher *tmp = (Teacher*)malloc(sizeof(Teacher));
    tmp->age = 20;
    *p = tmp;
    }
    //使用引用
    void getTeacher(Teacher* &p) {
    p = (Teacher*)malloc(sizeof(Teacher));
    p->age = 20;
    }

    void main() {
    Teacher *t = NULL;
    getTeacher(&t);
    system("pause");
    }

指针常量和常量指针

指针常量:就是不改变地址,但是可以改变它所指向的内容

常量指针:就是指向一个常量的指针,这个常量不可以被修改

1
2
3
4
5
6
7
8
9
10
11
12
void main() {
//指针常量
int a = 2, b = 3;
int *const p1 = &a;
//p1 = &b; 是错误的地址不能改变
*p1 = 4;

//常量指针
const int *p2 = &a;
p2 = &b;
//*p2 = 9; 是错误的,值不能改变
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class teacher {
public:
//构造函数,相当于activity中的onCreate
teacher();
//析构函数,相当于activity中的onDestory
~teacher();
private:
int age;

public:
void setage(int age){
this->age = 20;
}
int getage() {
return age;
}
};
//无参构造函数
teacher::teacher()
{
cout << "构造函数" << endl;
}
//有参构造函数,有参构造函数会覆盖默认的构造函数
teacher::teacher(char*name,int age)
{
cout << "构造函数" << endl;
}
teacher::~teacher()
{
cout << "析构函数" << endl;
}
int main() {
{
teacher t;
t.setage(20);
cout << t.getage() << endl;
}
system("pause");
}

除了构造函数,析构函数,C++中还有拷贝构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Teacher{
private:
char *name;
int age;
public:
Teacher(char *name, int age){
this->name = name;
this->age = age;
cout << "有参构造函数" << endl;
}
//拷贝构造函数(值拷贝)
//默认拷贝构造函数,就是值拷贝
Teacher(const Teacher &obj){
this->name = obj.name;
this->age = obj.age;
cout << "拷贝构造函数" << endl;
}
};

上面代码中的拷贝构造函数就是默认的拷贝构造函数,不写它也会存在。

默认的拷贝构造函数拷贝的是指。这样有时候会有问题,比如下面,将t2赋值给t2,他俩都指向同一块内存区域,当t1调用了它的析构函数释放了内存之后,t2在释放内存就会出错。这种称为浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Teacher{
private:
char *name;
int age;
public:
Teacher(char *name, int age){
this->name = (char*)malloc(100);
strcpy(this->name,name);
this->age = age;
cout << "有参构造函数" << endl;
}
~Teacher(){
cout << "析构" << endl;
//释放内存
free(this->name);
}
void myprint(){
cout << name << "," << age << endl;
}
};

void func(){
Teacher t1("rose", 20);
Teacher t2 = t1;
t2.myprint();
}

void main(){
func();

system("pause");
}

为了避免上面的问题,我们可以复写默认的拷贝构造函数,不拷贝值而是重新申请内存拷贝内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Teacher{
private:
char *name;
int age;
public:
Teacher(char *name, int age){
int len = strlen(name);
this->name = (char*)malloc(len+1);
strcpy(this->name, name);
this->age = age;
cout << "有参构造函数" << endl;
}
~Teacher(){
cout << "析构" << endl;
//释放内存
free(this->name);
}
//深拷贝
Teacher(const Teacher &obj){
//复制name属性
int len = strlen(obj.name);
this->name = (char*)malloc(len+1);
strcpy(this->name,obj.name);
this->age = obj.age;
}
void myprint(){
cout << name << "," << age << endl;
}
};

void func(){
Teacher t1("rose", 20);
Teacher t2 = t1;
t2.myprint();
}

void main(){
func();
system("pause");
}

C++可以和C混编,所以C++中也可以使用结构体,虽然跟C中一样也叫struct,不过对C中的struct做了一些扩展。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct teacher {
private:
int age;
public:
void setAge(int age) {
this->age = age;
}
int getAge() {
return age;
}
};

int main() {
{
struct teacher t;
t.setAge(20);
cout << t.getAge() << endl;
}
system("pause");
}

struct默认为public,class默认为private。

class可以用来声明模板,struct不可以。

前面只是简单的了解了类,C++中写一个类,一般先写一个头文件,在里面声明要写的类,然后写一个C++文件来实现这个类中的方法。最后在main中使用。

头文件:student.h

1
2
3
4
5
6
7
8
9
10
11
12
13
//保证只会被引用一次,防止重复引用
#pragma once;

class student {
public:
int age;
char* name;
public:
void setAge(int age);
int getAge();
void setName(char* name);
char* getName();
};

然后写student.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "student.h"

void student::setAge(int age) {
this->age = age;
}
int student::getAge() {
return this->age;
}
void student::setName(char* name) {
this->name = name;
}
char* student::getName() {
return this->name;
}

最后main中调用

1
2
3
4
5
6
7
8
9
10
int main() {
student s;
s.age = 20;
char name[] = "cha";
s.setName(name);

cout << s.getName() << endl;

system("pause");
}

构造函数的属性初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class Teacher {
private:
char* name;
public:
Teacher(char* name) {
this->name = name;
cout << "Teacher有参构造函数" << endl;
}
~Teacher() {
cout << "Teacher析构函数" << endl;
}
char* getName() {
return this->name;
}

};

class Student {
private:
int id;
//属性对象
Teacher t1;
Teacher t2;
public:
Student(int id, char *t1_n, char* t2_n) : t1(t1_n), t2(t2_n) {
this->id = id;
cout << "Student有参构造函数" << endl;
}
void myprint() {
cout << id << "," << t1.getName() << "," << t2.getName() << endl;
}
~Student() {
cout << "Student析构函数" << endl;
}
};

void func() {
char name[] = "cha";
Student s1(15, name, name);
//Student s2(20, name, name);
s1.myprint();
//s2.myprint();
}

void main() {
func();

system("pause");
}

怎么给Student中的成员变量Teacher中的name属性赋值呢?经过尝试是不能从构造方法中直接赋值的编译不通过,我们可以通过在构造函数后面加上一个冒号从后面赋值比如: t1(t1_n), t2(t2_n)

上面的代码,Student类中有Teacher类这个成员变量,那么他们的构造函数和析构函数式什么时候调用的呢?运行之后结果如下

1
2
3
4
5
6
7
Teacher有参构造函数
Teacher有参构造函数
Student有参构造函数
15,cha,cha
Student析构函数
Teacher析构函数
Teacher析构函数

可以看到,创建的时候,内部成员先调用构造函数,然后自身在调用。销毁的时候,自身先调用析构函数,内部成员在调用析构函数。

C++动态内存分配

  • c++中通过new关键字分配内存,通过delete关键字释放内存
  • c中通过malloc关键字申请内存,通过free关键字释放内存。

c++中也可以通过malloc和free来管理内存,跟第一种的区别是,通过new和delete管理,会执行构造函数和析构函数,而通过malloc和free来管理内存,不会执行构造函数和析构函数。

数组释放的时候,需要使用delete[]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
class Teacher {
private:
const char* name;
public:
Teacher(const char* name) {
this->name = name;
cout << "Teacher有参构造函数" << endl;
}
~Teacher() {
cout << "Teacher析构函数" << endl;
}
void setName(char* name) {
this->name = name;
}
const char* getName() {
return this->name;
}
};

void func() {
//C++
//会调用构造和析构函数
Teacher *t1 = new Teacher("jack");
cout << t1->getName() << endl;
//释放
delete t1;

//C
//Teacher *t2 = (Teacher*)malloc(sizeof(Teacher));
//t2->setName("jack");
//free(t2);

}

void main() {
func();
//数组类型
//C
//int *p1 = (int*)malloc(sizeof(int) * 10);
//p1[0] = 9;
//free(p1);

//C++
int *p2 = new int[10];
p2[0] = 2;
//释放数组 []
delete[] p2;

system("pause");
}

C++中的静态属性和方法

静态类型的属性不能再main函数中初始化,应该在全局初始化

访问的时候,可以通过类名直接访问,也可以通过类对象访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Teacher {
public:
char* name;
static int total;
public:
Teacher(char* name) {
this->name = name;
cout << "Teacher有参构造函数" << endl;
}
~Teacher() {
cout << "Teacher析构函数" << endl;
}
void setName(char* name) {
this->name = name;
}
char* getName() {
return this->name;
}
//静态函数
static void count() {
total++;
cout << total << endl;
}
};

//静态属性初始化赋值
int Teacher::total = 9;

void main() {
Teacher::total++;
cout << Teacher::total << endl;
//直接通过类名访问
Teacher::count();

//也可以通过对象名访问
Teacher t1((char*)"yuehang");
t1.count();

system("pause");
}

C++中类的大小

C/C++ 内存分区:栈、堆、全局(静态、全局)、常量区(字符串)、程序代码区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A{
public:
int i;
int j;
int k;
static int m;
};

class B{
public:
int i;
int j;
int k;
void myprintf(){
cout << "方法" << endl;
}
};

void main(){
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
system("pause");
}

上面的代码中打印的结果都是12。因为静态变量和函数都是共享的,不包含在类的里面。

既然函数是共享的,那怎么知道是谁调用了它呢,调用的时候传入this,就可以区别开了。this是当前对象的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class Teacher{
private:
char* name;
int age;
public:
Teacher(char* name,int age){
this->name = name;
this->age = age;
cout << "Teacher有参构造函数" << endl;
}
~Teacher(){
cout << "Teacher析构函数" << endl;
}
//常函数,修饰的是this
//既不能改变指针的值,又不能改变指针指向的内容
//const Teacher* const this
void myprint() const{
printf("%#x\n",this);
//改变属性的值是不行的
//this->name = "yuehang";
//改变this指针的值也是不行的
//this = (Teacher*)0x00009;
cout << this->name << "," << this->age << endl;
}
void myprint2(){
cout << this->name << "," << this->age << endl;
}
};

void main(){
Teacher t1((char*)"jack",20);
const Teacher t2((char*)"rose", 18);
//常量对象只能调用常量函数,不能调用非常量函数
//t2.myprint2();
//常函数,当前对象不能被修改,防止数据成员被非法访问
printf("%#x\n", &t1);
t1.myprint();

printf("%#x\n", &t2);
t2.myprint();

system("pause");
}

上面的代码中 ,使用const来修饰一个函数,这个函数就是一个常量函数。如果一个类中有常量函数,那么这个类的对象不能被修改。可以防止类中的属性被非法访问。

友元函数和友元类

友元函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A {
//友元函数
friend void modify_i(A *p, int a);
private:
int i;
public:
A(int i) {
this->i = i;
}
void modify_i(int a) {
this->i = a;
}
void myprint() {
cout << i << endl;
}

};
//友元函数的实现,在友元函数中可以访问私有的属性
void modify_i(A *p, int a) {
p->i = a;
}

void main() {
A* a = new A(10);
a->myprint();

//modify_i(a, 20);
//a->modify_i(20);
a->myprint();

system("pause");
}

正常情况下,A中的属性i是私有的,不能被外部修改,如果非得要修改的话,可以通过友元函数。使用friend关键字修饰,比如上面定义一个友元函数modify_i。通过这个函数就可以修改私有属性i了。

友元函数,我们可以在类的内部实现,也可以在外部实现,如上面的代码。

友元类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A {
//友元类
friend class B;
private:
int i;
public:
A(int i) {
this->i = i;
}
void myprint() {
cout << i << endl;
}
};

class B {
public:
//B这个友元类可以访问A类的任何成员
void accessAny() {
a.i = 30;
}
private:
A a;
};

友元类也是通过friend关键字修饰。在类A中声明友元类B,那么B中就可以访问和修改A中的私有变量了。

C++中的运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Point {
public:
int x;
int y;
public:
Point(int x = 0, int y = 0) {
this->x = x;
this->y = y;
}
//成员函数,运算符重载
Point operator+(Point &p2){
Point tmp(this->x + p2.x, this->y + p2.y);
return tmp;
}
void myprint() {
cout << x << "," << y << endl;
}
};

//重载+号
Point operator+(Point &p1, Point &p2) {
Point tmp(p1.x + p2.x, p1.y + p2.y);
return tmp;
}

void main() {
Point p1(10, 20);
Point p2(20, 10);

Point p3 = p1 + p2;
p3.myprint();

system("pause");
}

上面的代码中,执行p1+p2让结果p3中的数值是p1和p2中的数值之和。这时候就用到运算符的重载。使用operator关键字来修饰,上面例子中修饰了加号,我们同样可以修饰减号,乘号,除号等。

跟前面的友元函数一样,我们可以把重载函数写在类的里面,也可以写在外面,如上面代码中,取其一即可。

前面运算符重载的代码中,重载的属性x和y都是public(公有的),假如是私有的属性,外面不能访问这时候可以通过友元函数来完成运算符的重载比如:friend Point operator+(Point &p1, Point &p2);

继承多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Man {
public:
Man(char* name, int age) {
this->name = name;
this->age = age;
cout << "Man 构造函数" << endl;
}
~Man() {
cout << "Man 析构函数" << endl;
}
void sayHai() {
cout << "hai" << endl;
}
private:
char * name;
int age;
};
class Animal {

};

class Student : public Man , public Animal {
public:
Student(char* gride) :Man((char*)"jack",18) {
cout << "Student 构造函数" << endl;
}
~Student() {
cout << "Student 析构函数" << endl;
}
void study() {
cout << "学习" << endl;
}
void sayHai() {
cout << "Student hai" << endl;
}
private:
char *gride;
};

void func() {
//父类构造函数先调用
//子类的析构函数先调用
Student s((char*)"chs");
}
void main() {

func();

Student s((char*)"100");
s.sayHai();
s.Man::sayHai();
//1.父类类型的引用或指针
Man *m_p = &s;
m_p->sayHai();

Man &m = s;
m.sayHai();
system("pause");
}
  1. C++中继承使用冒号来修饰,跟java不一样,C++中可以多继承。比如下面代码,Student继承自人和动物。
  2. 子类向构造方法传参,java中可以直接使用super关键字,c++中没有,想要传参需要在子类构造方法后面传比如: Man((char*)"jack",18)
  3. 父类的构造函数先执行,子类的析构函数先执行。
  4. 如果子类中有跟父类同名的方法,比如上面代码中的sayHai方法,使用子类对象调用的时候调用的是子类的sayHai,如果想调用父类的此函数,需要指定父类显示调用如s.Man::sayHai();
  5. 上面代码可以看到,继承的时候父类前面都加了访问修饰符。一般情况下都使用public,默认是private
基类 继承方式 子类
public public继承 public
public protected继承 protected
public private继承 private
protected public继承 protected
protected protected继承 protected
protected private继承 private
private public继承 子类无权访问
private protected继承 子类无权访问
private private继承 子类无权访问

虚继承和虚函数

虚继承: 不同路径继承来的同名成员只有一份拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class A{
public:
char* name;
};

class A1 : virtual public A{

};

class A2 : virtual public A{

};

class B : public A1, public A2{

};

void main(){
B b;
b.name = "大海";
//如果没有 virtual关键字只能 指定父类显示调用
//b.A1::name = "大海";
//b.A2::name = "大海";
system("pause");
}

上面的代码中A1,A2都继承A,B继承了A1和A2,A中有个name属性,那么如果我们调用b.name就会报错,因为系统不知道你是调用A1的还是A2的。这时候只能显示的调用指定调用哪一个的比如b.A1::name = "大海"

正常情况下我们希望直接使用b.name就能调用,这时候继承的时候使用virtual关键字来修饰A,就可以了,这是虚继承。

虚函数: 相当于java中的多态。使用多态可以增加程序的可扩展性,使用virtual关键字修饰函数

动态多态:程序运行过程中,觉得哪一个函数被调用(重写)

静态多态:重载

能够使用多态的条件:

  1. 使用了继承
  2. 父类的引用指向子类的实现
  3. 子类重写了父类的函数

例子:

定义一个动物基类

1
2
3
4
5
6
7
#pragma once

class Animal {
public:
virtual void eat();
virtual void drink();
};

1
2
3
4
5
6
7
8
9
10
#include "Animal.h"
#include <iostream>
using namespace std;

void Animal::eat(){
cout << "吃" << endl;
}
void Animal::drink() {
cout << "喝" << endl;
}

定义一个兔子继承动物

1
2
3
4
5
6
7
#pragma once
#include "Animal.h"

class Rabbit : public Animal {
virtual void eat();
virtual void drink();
};

1
2
3
4
5
6
7
8
9
10
#include "Rabbit.h"
#include <iostream>
using namespace std;

void Rabbit::eat(){
cout << "兔子吃" <<endl;
}
void Rabbit::drink() {
cout << "兔子喝" << endl;
}

使用:业务函数中需要传入一个基类Animal,使用的时候,Animal的子类和和它自己都能传入,使用虚函数之后,就可以调用自己的相关方法了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <iostream>
#include "Animal.h"
#include "Rabbit.h"

using namespace std;

//业务函数
void bizAnimal(Animal &a) {
a.eat();
a.drink();
}

void main() {
Animal a;
bizAnimal(a);

Rabbit r;
bizAnimal(r);
system("pause");
}

纯虚函数

纯虚函数相当于java中的抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Shape{
public:
//纯虚函数
virtual void sayArea() = 0;
void print(){
cout << "hi" << endl;
}
};
//圆
class Circle : public Shape{
public:
Circle(int r){
this->r = r;
}
void sayArea(){
cout << "圆的面积:" << (3.14 * r * r) << endl;
}
private:
int r;
};

void main(){
//Shape s;
Circle c(10);

system("pause");
}
  1. 当一个类有一个纯虚函数的时候,这个类就是一个抽象类。
  2. 抽象类不能实例化对象
  3. 子类继承抽象类,必须要实现纯虚函数,如果没有实现,那么子类也是抽象类。
  4. 接口跟抽象类的写法一模一样,只是用到的时候,逻辑上叫它接口。

模板函数和模板类

模板函数相当于java中的泛型,泛型主要解决业务逻辑一样,数据类型不一样的问题

比如下面代码

1
2
3
4
5
6
7
8
9
10
11
12
13
void swap(int& a,int& b){
int tmp = 0;
tmp = a;
a = b;
b = tmp;
}

void swap(char& a, char& b){
char tmp = 0;
tmp = a;
a = b;
b = tmp;
}

这两个函数都是实现的一样的功能就是交换,只是传入的数据不一样,这时候就可以抽取一个模板。使用template关键字来修饰,让类型自动推导。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template <typename T>
void swap(T& a, T& b){
T tmp = 0;
tmp = a;
a = b;
b = tmp;
}
void main(){
//根据实际类型,自动推导
int a = 10, b = 20;
swap<int>(a,b);
cout << a << "," << b << endl;

char x = 'v', y = 'w';
swap(x, y);
cout << x << "," << y << endl;

system("pause");
}

模板类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//模板类
template<class T>
class A{
public:
A(T a){
this->a = a;
}
protected:
T a;
};

//普通类继承模板类
class B : public A<int>{
public:
B(int a,int b) : A<int>(a){
this->b = b;
}
private:
int b;
};

//模板类继承模板类
template <class T>
class C : public A<T>{
public:
C(T c, T a) : A<T>(a){
this->c = c;
}
protected:
T c;
};

void main(){
//实例化模板类对象
//List<String> list;
A<int> a(6);
system("pause");
}

异常

C++ 异常处理,根据抛出的异常数据类型,进入到相应的catch块中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void main() {
try {
int age = 300;
if (age > 200) {
throw "10.0";
}
}
catch (int a) {
cout << "int异常" << endl;
}
catch (const char *b) {
cout << b << endl;
}
catch (...) {
cout << "未知异常" << endl;
}
system("pause");
}

上面代码中我们throw什么类型的异常,就会进入对应的下面catch中。catch (...)代表捕获为止异常。

我们也可以自定义自己的异常类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyException {

};
void mydiv(int a, int b) {
if (b == 0) {
throw MyException();
//不要抛出异常指针
//throw new MyException;
}
}
void main() {
try {
mydiv(8, 0);
}
catch (MyException& e2) {
cout << "MyException引用" << endl;
}
catch (MyException* e1) {
cout << "MyException指针" << endl;
delete e1;
}
system("pause");
}

上面代码中,定义自己的异常类MyException,抛出异常的时候可以直接throw MyException()然后捕获。也可以使用/throw new MyException不过通过new的这种方式抛出的是异常的指针,捕获到之后还要释放内存,所以不要使用这种方式太麻烦。

C++ 标准的异常:

C++ 提供了一系列标准的异常,这些类都是 exception 类派生而来的 中,我们可以在程序中使用这些标准的异常。

异常 描述
std::exception 该异常是所有标准 C++ 异常的父类。
std::bad_alloc 该异常可以通过 new 抛出。
std::bad_cast 该异常可以通过 dynamic_cast 抛出。
std::bad_exception 这在处理 C++ 程序中无法预期的异常时非常有用。
std::bad_typeid 该异常可以通过 typeid 抛出。
std::logic_error 理论上可以通过读取代码来检测到的异常。
std::domain_error 当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument 当使用了无效的参数时,会抛出该异常。
std::length_error 当创建了太长的 std::string 时,会抛出该异常。
std::out_of_range 该异常可以通过方法抛出,例如 std::vector 和 std::bitset<>::operator
std::runtime_error 理论上不可以通过读取代码来检测到的异常。
std::overflow_error 当发生数学上溢时,会抛出该异常。
std::range_error 当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error 当发生数学下溢时,会抛出该异常。

我们也可以通过集成exception来自定义一个异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdexcept>
//自定义(类似于JavaNullPointerException)
class NullPointerException : public exception {
public:
NullPointerException(const char* msg) : exception(msg) {

}
};

void mydiv(int a, int b) {
if (b > 10) {
//标准异常
throw out_of_range("超出范围");
}
else if (b == NULL) {
throw NullPointerException("空指针");
}
else if (b == 0) {
//标准异常
throw invalid_argument("参数不合法");
}
}
void main() {
try {
mydiv(8, NULL);
}
catch (out_of_range e1) {
cout << e1.what() << endl;
}
catch (NullPointerException& e2) {
cout << e2.what() << endl;
}
catch (...) {
}
system("pause");
}

类型的转换

C++提供了4中类型转换操作符

  1. const_cast:修改类型的const或volatile属性
  2. static_cast:静态类型转换,如int转换成char,指针与void之间互转。如:float转成void、Bean转成void、函数指针转成void*等;子类指针/引用与 父类指针/引用 转换。
  3. dynamic_cast:动态类型转换,比如子类和父类之间多态类型的转换
  4. reinterpret_cast:对指针、引用进行原始转换

使用格式:

TYPE B = static_cast(TYPE)(a)

类型强制转换的时候,其实我们可以直接使用(TYPE)这种形式转换比如int i; double j = (int)i,C++吧转换类型细化,可以让意图更加明显,可读性更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void* func(int type){	
switch (type){
case 1: {
int i = 9;
return &i;
}
case 2: {
int a = 'X';
return &a;
}
default:{
return NULL;
}

}
}

void func2(char* c_p){
cout << *c_p << endl;
}

void main(){
//int i = 0;
自动转换 可读性不高
//double d = i;
//double d = 9.5;
//int i = d;

int i = 8;
double d = 9.5;
//意图明显 可读性高
i = static_cast<int>(d);

//void* -> char*
//char* c_p = (char*)func(2);
//char* c_p = static_cast<char*>(func(2));

//C++ 意图明显
func2(static_cast<char*>(func(2)));
//C
func2((char*)(func(2)));

system("pause");
}

假如我们想要修改一个使用const修饰的常量,正常情况下无法修改,只能通过指针间接的去修改,但是别人阅读起这个代码来就比较费劲了。而使用const_cast,意图明显就更加容易理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void func(const char c[]){
//通过指针间接赋值,其他人并不知道,这次转型是为了去常量
//char* c_p = (char*)c;
//c_p[1] = 'X';
//提高了可读性
char* c_p = const_cast<char*>(c);
c_p[1] = 'Y';
cout << c << endl;
}
void main(){
char c[] = "hello";
func(c);
system("pause");
}

子类和父类之间多态类型的转换使用dynamic_cast来提高可读性,防止不可预见的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Person{
public:
virtual void print(){
cout << "人" << endl;
}
};

class Boy : public Person{
public:
void print(){
cout << "boy" << endl;
}

void chasing(){
cout << "泡妞" << endl;
}
};

class Girl : public Person{
public:
void print(){
cout << "girl" << endl;
}

void carebaby(){
cout << "生孩子" << endl;
}
};

void func(Person* obj){
//调用子类的特有的函数,转为实际类型
//并不知道转型失败
//Boy* b = (Boy*)obj;
//b->print();

//转型失败,返回NULL
Boy* b = dynamic_cast<Man*>(obj);
if (b != NULL){
b->chasing();
}

Girl* w = dynamic_cast<Woman*>(obj);
if (g != NULL){
g->carebaby();
}
}

void main(){
//Girl g1;
//Person *p1 = &g1;

//func(p1);

Girl g2;
Girl* w_p = &g2;
system("pause");
}

上的代码中,一个函数中需要传入一个Person对象,方法内部使用原始转换方式强制转换成Boy对象。加入我们传入一个Girl对象进来,其实是转型失败的。使用原始方式即使转型失败我们也察觉不到。使用dynamic_cast,如果转型失败会返回一个NULL值。

使用reinterpret_cast来转型函数指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using namespace std;

void func1(){
cout << "func1" << endl;
}

char* func2(){
cout << "func2" << endl;
return "abc";
}
//定义函数指针类型
typedef void(*f_p)();

void main(){
//函数指针数组
f_p f_array[6];
//赋值
f_array[0] = func1;

//C方式
//f_array[1] = (f_p)(func2);
//C++方式
f_array[1] = reinterpret_cast<f_p>(func2);

f_array[1]();

system("pause");
}

C++中的io操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

#include <fstream>

//IO流
//文本文件操作

void main(){
char* fname = "c://dest.txt";
//输出流
ofstream fout(fname);
//创建失败
if (fout.bad()){
return;
}

fout << "大海" << endl;
fout << "哈哈哈" << endl;
//关闭
fout.close();
//读取
ifstream fin(fname);
if (fin.bad()){
return;
}
char ch;
while (fin.get(ch)){
//输出
cout << ch;
}
fin.close();

system("pause");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

#include <fstream>
//二进制文件
void main(){
char* src = "c://test.jpg";
char* dest = "c://test_1.jpg";
//输入流
ifstream fin(src, ios::binary);
//输出流
ofstream fout(dest, ios::binary);

if (fin.bad() || fout.bad()){
return;
}
//end of file
while (!fin.eof()){
//读取
char buff[1024] = {0};
fin.read(buff,1024);

//写入
fout.write(buff,1024);
}
//关闭
fin.close();
fout.close();

system("pause");
}

C++对象的持久化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Person
{
public:
Person()
{

}
Person(char * name, int age)
{
this->name = name;
this->age = age;
}
void print()
{
cout << name << "," << age << endl;
}
private:
char * name;
int age;
};


void main()
{
Person p1("老蔡", 22);
Person p2("老吴", 18);
//输出流
ofstream fout("c://obj.data", ios::binary);
fout.write((char*)(&p1), sizeof(Person)); //指针能够读取到正确的数据,读取内存区的长度
fout.write((char*)(&p2), sizeof(Person));
fout.close();

//输入流
ifstream fin("c://obj.data", ios::binary);
Person tmp;
//从哪里读 督导哪里结束 传入一个指针来操作加载进来的对象
fin.read((char*)(&tmp), sizeof(Person));
tmp.print();

fin.read((char*)(&tmp), sizeof(Person));
tmp.print();

system("pause");

}

C++标准模板库

(stl standard template library )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <string>
using namespace std;
void main()
{
string s1 = "craig david";
string s2(" 7 days");
string s3 = s1 + s2;

cout << s3 << endl;

//转c字符串
const char* c_str = s3.c_str();
cout << c_str << endl;
//s1.at(2);

system("pause");
}

序列式容器:元素的排列顺序与元素本身无关,由添加的顺序决定。

序列容器一般有:List,vertor,queue,dequeue,stack,priority queue,

关联式容器:如set,map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <vector>

void main()
{
//动态数组
//不需要使用动态内存分配,就可以使用动态数组
vector<int> v;
v.push_back(12);
v.push_back(10);
v.push_back(5);

for (int i = 0; i < v.size(); i++)
{
cout << v[i] << endl;
}
//返回第一个元素
v.front()
//返回最后一个元素
v.back()
//清空
v.clear()
//删除指定元素
v.erase(v.begin(),b.end())

system("pause");
}
# c/c++

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×