黎明晨光

生活从一点一滴开始


  • 首页

  • 标签

  • 分类

  • 归档

  • 搜索

核函数

发表于 2017-09-02 | 分类于 数学基础 , 高数

之前分析的感知机、主成分分析(Principle component analysis, PCA)包括后面看的支撑向量机(Support vector machines, SVM),都有用到核函数。核函数是将信号映射到高维,而PCA一般用来降维。这里简单梳理一下核函数的知识:

核函数基本概念

定义

设$\chi $是输入空间(欧氏空间$\mathbb{R}^{n}$的子集或离散集合),又设$\mathbb{R}^{H}$为特征空间(希尔伯特空间),如果存在一个从$\chi $到$\mathbb{R}^{H}$的映射使得对所有$x,z\in \chi $,函数$K(x,z)$满足条件则称$K(x,z)$为核函数,$\phi (x)$为映射函数,式中$\phi (x) \cdot \phi (z)$为$\phi (x)$和$\phi (z)$的内积

核函数:是映射关系$\phi (x)$的内积,映射函数本身仅仅是一种映射关系,并没有增加维度的特性,不过可以利用核函数的特性,构造可以增加维度的核函数,这通常是我们希望的。

核函数的作用

  • 聚类、分类


二维映射到三维,区分就更容易了,这是聚类、分类常用核函数的原因。为什么PCA这样一个降维算法也用核函数呢?

  • 降维

左图为原数据,右图为映射到三维的数据,可以看出:同样是降到1维,先通过Kernel映射到(Kernel是映射的内积,不要弄乱了)三维,再投影到1维,就容易分离开,这就是Kernel在PCA降维中的应用,本质还是对原有数据增加维度。

核函数为什么可以映射到高维

为什么实现数据映射到高维

  • 设原空间数据点$a_1=(x_1,x_2);a_2=(x_1’,x_2’)$;
  • 设高维空间的数据点为$A_1 =\phi (a_1)=(z_1,z_2,z_3);A_2=\phi (a_1)=(z_1’,z_2’,z_3’)$

$\left \langle a_1,a_2 \right \rangle$为两点之间的内积$\left \langle a_1,a_2 \right \rangle = \left \langle (x_1,x_2),(x_1’,x_2’) \right \rangle =x_1x_1’+x_2x_2’$

$<\phi (a_1),\phi (a_2)>=<\phi (x_1,x_2),\phi (x_1’,x_2’)>=<(z_1,z_2,z_3),(z_1’,z_2’,z_3’)>=<(x_1^2,\sqrt{2}x_1x_2,x_2^2),({x}_1’^2,\sqrt{2}x_1’x_2’,{x}_2’^2)>=x_1^2x_1’^2+2x_1x_2x_1’x_2’+x_2^2x_2’^2=(x_1x_1’+x_2x_2’)^2=(<a_1,a_2’>)^2=k(a_1,a_2)$

为什么不用映射函数$\phi (x)$,而用他们的内积形式$K(x,z)$,即Kernel函数?

因为$(x,z)$一起出现的时候,$K(x,z)=\phi (x) \cdot \phi (z)$有许多固定的形式可以调用,而不必求解或者关心$\phi (x)$的具体形式,这大大简化了求解。

核函数的用法

  1. 两点之间的距离

  2. 两点之间的角度

什么样的函数才可以叫做核函数?

对称函数$K(x,z)$为正定核的充要条件如下:对任意$x_i\in \chi ,i=1,2,\cdots ,m$,任意正整数$m$,对称函数$K(x,z)$对应的Gram矩阵是半正定。

半正定:$x^T\: Gram\; x\geqslant 0$

常用核函数

核函数 英文 公式
线性核函数 Linear Kernel $K(x, z) = x \cdot z$
多项式核函数 Polynomial Kernel $K(x, z) = (\gamma x \cdot z + r)^d$
高斯核函数 Gaussian Kernel $K(x, z) = exp(-\gamma$||$x-z$||$^2)$
Sigmoid核函数 Sigmoid Kernel $K(x, z) = tanh(\gamma x \cdot z + r)$
  • 线性核函数
    线性核函数(Linear Kernel)其实就是我们前两篇的线性可分SVM,表达式为:也就是说,线性可分SVM我们可以和线性不可分SVM归为一类,区别仅仅在于线性可分SVM用的是线性核函数。
  • 多项式核函数
    多项式核函数(Polynomial Kernel)是线性不可分SVM常用的核函数之一,表达式为:其中,$γ,r,d$都需要自己调参定义。
  • 高斯核函数
    高斯核函数(Gaussian Kernel),在SVM中也称为径向基核函数(Radial Basis Function,RBF),它是非线性分类SVM最主流的核函数。libsvm默认的核函数就是它。表达式为:其中,$γ$大于0,需要自己调参定义。
  • Sigmoid核函数
    Sigmoid核函数(Sigmoid Kernel)也是线性不可分SVM常用的核函数之一,表达式为:其中,$γ,r$都需要自己调参定义。

C++重载运算符和重载函数

发表于 2017-08-29 | 分类于 编程语言 , 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
24
25
26
27
28
29
30
31
32
33
#include <iostream>
using namespace std;

class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}

void print(double f) {
cout << "浮点数为: " << f << endl;
}

void print(char c[]) {
cout << "字符串为: " << c << endl;
}
};

int main(void)
{
printData pd;

// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);

return 0;
}

C++ 中的运算符重载

重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。

一元运算符

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
#include <iostream>
using namespace std;

class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches << endl;
}
// 重载负运算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
};
int main()
{
Distance D1(11, 10), D2(-5, 11);

D1.displayDistance();
D2.displayDistance();

-D1; // 取相反数
D1.displayDistance(); // 距离 D1

-D2; // 取相反数
D2.displayDistance(); // 距离 D2

return 0;
}
  • 输出
    1
    2
    3
    4
    F: 11 I:10
    F: -5 I:11
    F: -11 I:-10
    F: 5 I:-11

二元运算符

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
using namespace std;

class Box
{
public:

double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}

void setBreadth( double bre )
{
breadth = bre;
}

void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中

// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);

// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);

// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;

// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;

// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;

// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;

return 0;
}

关系运算符重载

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
#include <iostream>
using namespace std;

class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches <<endl;
}
// 重载负运算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
// 重载小于运算符( < )
bool operator <(const Distance& d)
{
if(feet < d.feet)
{
return true;
}
if(feet == d.feet && inches < d.inches)
{
return true;
}
return false;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11);

if( D1 < D2 )
{
cout << "D1 is less than D2 " << endl;
}
else
{
cout << "D2 is less than D1 " << endl;
}
return 0;
}

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
#include <iostream>
using namespace std;

class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
friend ostream &operator<<(ostream &output,
const Distance &D)
{
output << "F : " << D.feet << " I : " << D.inches;
return output;
}

friend istream &operator>>(istream &input, Distance &D)
{
input >> D.feet >> D.inches;
return input;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11), D3;

cout << "Enter the value of object : " << endl;
cin >> D3;
cout << "First Distance : " << D1 << endl;
cout << "Second Distance :" << D2 << endl;
cout << "Third Distance :" << D3 << endl;

return 0;
}

可重载运算符

类别 运算符
双目算术运算符 + (加),-(减),*(乘),/(除),% (取模)
关系运算符 ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)
逻辑运算符 ||(逻辑或),&&(逻辑与),|(逻辑非)
单目运算符 + (正),-(负),*(指针),&(取地址)
自增自减运算符 ++(自增),—(自减)
位运算符 | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)
赋值运算符 =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=
空间申请与释放 new, delete, new[ ] , delete[]
其他运算符 ()(函数调用),->(成员访问),,(逗号),

参考

C++继承

发表于 2017-08-29 | 分类于 编程语言 , C++

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行时间的效果。

当创建一个类时,您不需要重新编写新的数据成员和成员函数,只需指定新建的类继承了一个已有的类的成员即可。这个已有的类称为基类,新建的类称为派生类。

阅读全文 »

C++类和对象

发表于 2017-08-29 | 分类于 编程语言 , 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
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

#include<iostream>
using namespace std;

#include <iostream>

using namespace std;

class Box
{
//属性
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度

double volume()
{
return height * length * breadth;
}

};
//方法
int main()
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
double volume = 0.0; // 用于存储体积

// box 1 详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;

// box 2 详述
Box2.height = 10.0;
Box2.length = 12.0;
Box2.breadth = 13.0;

// box 1 的体积
volume = Box1.volume();
cout << "Box1 的体积:" << volume << endl;

// box 2 的体积
volume = Box2.volume();
cout << "Box2 的体积:" << volume << endl;
return 0;
}

C++ 类访问修饰符

数据封装是面向对象编程的一个重要特点,它防止函数直接访问类类型的内部成员(属性和方法)。

修饰符 描述
public 公有成员在程序中类的外部是可访问的。您可以不使用任何成员函数来设置和获取公有变量的值。
private 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。默认情况下,类的所有成员都是私有的。
protected 保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Base {

public:

// 公有成员

protected:

// 受保护成员

private:

// 私有成员

};

公有(public)成员

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
#include <iostream>

using namespace std;

class Line
{
public:
double length;
void setLength( double len );
double getLength( void );
};

// 成员函数定义
double Line::getLength(void)
{
return length ;
}

void Line::setLength( double len )
{
length = len;
}

// 程序的主函数
int main( )
{
Line line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;

// 不使用成员函数设置长度
line.length = 10.0; // OK: 因为 length 是公有的
cout << "Length of line : " << line.length <<endl;
return 0;
}

私有(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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>

using namespace std;

class Box
{
public:
double length;
void setWidth( double wid );
double getWidth( void );

private:
double width;
};

// 成员函数定义
double Box::getWidth(void)
{
return width ;
}

void Box::setWidth( double wid )
{
width = wid;
}

// 程序的主函数
int main()
{
Box box;

// 不使用成员函数设置长度
box.length = 10.0; // OK: 因为 length 是公有的
cout << "Length of box : " << box.length <<endl;

// 不使用成员函数设置宽度
// box.width = 10.0; // Error: 因为 width 是私有的
box.setWidth(10.0); // 使用成员函数设置宽度
cout << "Width of box : " << box.getWidth() <<endl;

return 0;
}

保护(protected)成员

保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。

在下一个章节中,您将学习到派生类和继承的知识。现在您可以看到下面的实例中,我们从父类 Box 派生了一个子类 smallBox。
下面的实例与前面的实例类似,在这里 width 成员可被派生类 smallBox 的任何成员函数访问。

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
#include <iostream>
using namespace std;

class Box
{
protected:
double width;
};

class SmallBox:Box // SmallBox 是派生类
{
public:
void setSmallWidth( double wid );
double getSmallWidth( void );
};

// 子类的成员函数
double SmallBox::getSmallWidth(void)
{
return width ;
}

void SmallBox::setSmallWidth( double wid )
{
width = wid;
}

// 程序的主函数
int main( )
{
SmallBox box;

// 使用成员函数设置宽度
box.setSmallWidth(5.0);
cout << "Width of box : "<< box.getSmallWidth() << endl;

return 0;
}

继承的修饰符

有public, protected, private三种继承方式,它们相应地改变了基类成员的访问属性。但无论哪种继承方式,上面两点都没有改变

  • private 成员只能被本类成员(类内)和友元访问,不能被派生类访问;
  • protected 成员可以被派生类访问。
继承模式 描述
public 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:public, protected, private
protected 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:protected, protected, private
private 继承 基类 public 成员,protected 成员,private 成员的访问属性在派生类中分别变成:private, private, private

public 继承

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
#include<iostream>
#include<assert.h>
using namespace std;

class A{
public:
int a;
A(){
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun(){
cout << a << endl; //正确
cout << a1 << endl; //正确
cout << a2 << endl; //正确
cout << a3 << endl; //正确
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : public A{
public:
int a;
B(int i){
A();
a = i;
}
void fun(){
cout << a << endl; //正确,public成员
cout << a1 << endl; //正确,基类的public成员,在派生类中仍是public成员。
cout << a2 << endl; //正确,基类的protected成员,在派生类中仍是protected可以被派生类访问。
cout << a3 << endl; //错误,基类的private成员不能被派生类访问。
}
};
int main(){
B b(10);
cout << b.a << endl;
cout << b.a1 << endl; //正确
cout << b.a2 << endl; //错误,类外不能访问protected成员
cout << b.a3 << endl; //错误,类外不能访问private成员
system("pause");
return 0;
}

protected 继承

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
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
int a;
A(){
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun(){
cout << a << endl; //正确
cout << a1 << endl; //正确
cout << a2 << endl; //正确
cout << a3 << endl; //正确
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : protected A{
public:
int a;
B(int i){
A();
a = i;
}
void fun(){
cout << a << endl; //正确,public成员。
cout << a1 << endl; //正确,基类的public成员,在派生类中变成了protected,可以被派生类访问。
cout << a2 << endl; //正确,基类的protected成员,在派生类中还是protected,可以被派生类访问。
cout << a3 << endl; //错误,基类的private成员不能被派生类访问。
}
};
int main(){
B b(10);
cout << b.a << endl; //正确。public成员
cout << b.a1 << endl; //错误,protected成员不能在类外访问。
cout << b.a2 << endl; //错误,protected成员不能在类外访问。
cout << b.a3 << endl; //错误,private成员不能在类外访问。
system("pause");
return 0;
}

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<assert.h>
using namespace std;
class A{
public:
int a;
A(){
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun(){
cout << a << endl; //正确
cout << a1 << endl; //正确
cout << a2 << endl; //正确
cout << a3 << endl; //正确
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : private A{
public:
int a;
B(int i){
A();
a = i;
}
void fun(){
cout << a << endl; //正确,public成员。
cout << a1 << endl; //正确,基类public成员,在派生类中变成了private,可以被派生类访问。
cout << a2 << endl; //正确,基类的protected成员,在派生类中变成了private,可以被派生类访问。
cout << a3 << endl; //错误,基类的private成员不能被派生类访问。
}
};
int main(){
B b(10);
cout << b.a << endl; //正确。public成员
cout << b.a1 << endl; //错误,private成员不能在类外访问。
cout << b.a2 << endl; //错误, private成员不能在类外访问。
cout << b.a3 << endl; //错误,private成员不能在类外访问。
system("pause");
return 0;
}

构造函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。

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
#include <iostream>

using namespace std;

class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数

private:
double length;
};

// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}

void Line::setLength( double len )
{
length = len;
}

double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;

return 0;
}

带参数的构造函数

默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值,如下面的例子所示:

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
#include <iostream>

using namespace std;

class Line
{
public:
void setLength( double len );
double getLength( void );
Line(double len); // 这是构造函数

private:
double length;
};

// 成员函数定义,包括构造函数
Line::Line( double len)
{
cout << "Object is being created, length = " << len << endl;
length = len;
}

void Line::setLength( double len )
{
length = len;
}

double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line(10.0);

// 获取默认设置的长度
cout << "Length of line : " << line.getLength() <<endl;
// 再次设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;

return 0;
}

类的析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

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
#include <iostream>

using namespace std;

class Line
{
public:
void setLength(double len);
double getLength(void);
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明

private:
double length;
};

// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}

void Line::setLength(double len)
{
length = len;
}

double Line::getLength(void)
{
return length;
}
// 程序的主函数
int main()
{
Line line;

// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;

return 0; //结束后,对象销毁,此时返回析构函数
}

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>

using namespace std;

class Line
{
public:
int getLength( void );
Line( int len ); // 简单的构造函数
Line( const Line &obj); // 拷贝构造函数
~Line(); // 析构函数

private:
int *ptr;
};

// 成员函数定义,包括构造函数
Line::Line(int len)
{
cout << "调用构造函数" << endl;
// 为指针分配内存
ptr = new int;
*ptr = len;
}

Line::Line(const Line &obj)
{
cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
ptr = new int;
*ptr = *obj.ptr; // 拷贝值
}

Line::~Line(void)
{
cout << "释放内存" << endl;
delete ptr;
}
int Line::getLength( void )
{
return *ptr;
}

void display(Line obj)
{
cout << "line 大小 : " << obj.getLength() <<endl;
}

// 程序的主函数
int main( )
{
Line line1(10);

Line line2 = line1; // 这里也调用了拷贝构造函数

display(line1);
display(line2);

return 0;
}

C++ 友元函数

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:

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
#include <iostream>

using namespace std;

class Box
{
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};

// 成员函数定义
void Box::setWidth( double wid )
{
width = wid;
}

// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
cout << "Width of box : " << box.width <<endl;
}

// 程序的主函数
int main( )
{
Box box;

// 使用成员函数设置宽度
box.setWidth(10.0);

// 使用友元函数输出宽度
printWidth( box );

return 0;
}

C++内联函数

调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行
C++中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline 放在函数定义(注意是定义而非声明,下文继续讲到)的前面即可将函数指定为内联函数。内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数。
如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

using namespace std;

inline int Max(int x, int y)
{
return (x > y)? x : y;
}

// 程序的主函数
int main( )
{

cout << "Max (20,10): " << Max(20,10) << endl;
cout << "Max (0,200): " << Max(0,200) << endl;
cout << "Max (100,1010): " << Max(100,1010) << endl;
return 0;
}

C++ this 指针

在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

友元函数没有 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
44
#include <iostream>

using namespace std;

class Box
{
public:
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2

if(Box1.compare(Box2))
{
cout << "Box2 is smaller than Box1" <<endl;
}
else
{
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}

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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>

using namespace std;

class Box
{
public:
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};

int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
Box *ptrBox; // Declare pointer to a class.

// 保存第一个对象的地址
ptrBox = &Box1;

// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box1: " << ptrBox->Volume() << endl;

// 保存第二个对象的地址
ptrBox = &Box2;

// 现在尝试使用成员访问运算符来访问成员
cout << "Volume of Box2: " << ptrBox->Volume() << endl;

return 0;
}

C++类的静态成员

我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。

静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化,如下面的实例所示。

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
#include <iostream>

using namespace std;

class Box
{
public:
static int objectCount;
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// 每次创建对象时增加 1
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{
Box Box1(3.3, 1.2, 1.5); // 声明 box1
Box Box2(8.5, 6.0, 2.0); // 声明 box2

// 输出对象的总数
cout << "Total objects: " << Box::objectCount << endl;

return 0;
}
  • 输出
    1
    2
    3
    Constructor called.
    Constructor called.
    Total objects: 2

静态成员函数

  • 如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问。
  • 静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。
  • 静态成员函数有一个类范围,他们不能访问类的 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
44
45
46
47
48
49
#include <iostream>

using namespace std;

class Box
{
public:
static int objectCount;
// 构造函数定义
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
// 每次创建对象时增加 1
objectCount++;
}
double Volume()
{
return length * breadth * height;
}
static int getCount()
{
return objectCount;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{

// 在创建对象之前输出对象的总数
cout << "Inital Stage Count: " << Box::getCount() << endl;

Box Box1(3.3, 1.2, 1.5); // 声明 box1
Box Box2(8.5, 6.0, 2.0); // 声明 box2

// 在创建对象之后输出对象的总数
cout << "Final Stage Count: " << Box::getCount() << endl;

return 0;
}
  • 输出
    1
    2
    3
    4
    Inital Stage Count: 0
    Constructor called.
    Constructor called.
    Final Stage Count: 2

C++结构体

发表于 2017-08-29 | 分类于 编程语言 , C++

结构体声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2; //stu1与stu2为结构体变量

//给结构体成员赋值
stu1.name = "Tom";
stu1.num = 12;
stu1.age = 18;
stu1.group = 'A';
stu1.score = 136.5;
阅读全文 »

C++引用

发表于 2017-08-29 | 分类于 编程语言 , C++

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。

阅读全文 »

C++指针

发表于 2017-08-29 | 分类于 编程语言 , C++

概括

1
2
3
4
5
int *p    //声明指针变量
int i //声明变量
int temp //声明变量temp
p=&i //&为取地址符,获取变量i的地址
temp = *p //获取地址p的值
阅读全文 »

C++数组

发表于 2017-08-29 | 分类于 编程语言 , C++

数组

1
2
3
type arrayName [ arraySize ];

double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
阅读全文 »

C++数字

发表于 2017-08-29 | 分类于 编程语言 , C++

数学运算

为了利用这些函数,您需要引用数学头文件 。

序号 函数 & 描述
1 double cos(double);
该函数返回弧度角(double 型)的余弦。
2 double sin(double);
该函数返回弧度角(double 型)的正弦。
3 double tan(double);
该函数返回弧度角(double 型)的正切。
4 double log(double);
该函数返回参数的自然对数。
5 double pow(double, double);
假设第一个参数为 x,第二个参数为 y,则该函数返回 x 的 y 次方。
6 double hypot(double, double);
该函数返回两个参数的平方总和的平方根,也就是说,参数为一个直角三角形的两个直角边,函数会返回斜边的长度。
7 double sqrt(double);
该函数返回参数的平方根。
8 int abs(int);
该函数返回整数的绝对值。
9 double fabs(double);
该函数返回任意一个十进制数的绝对值。
10 double floor(double);
该函数返回一个小于或等于传入参数的最大整数。
阅读全文 »

C++函数

发表于 2017-08-29 | 分类于 编程语言 , C++
1
2
3
4
return_type function_name( parameter list )
{
body of the function
}
  • 返回类型:一个函数可以返回一个值。return_type 是函数返回的值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type 是关键字 void。
  • 函数名称:这是函数的实际名称。函数名和参数列表一起构成了函数签名。
  • 参数:参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可能不包含参数。
  • 函数主体:函数主体包含一组定义函数执行任务的语句
阅读全文 »
1…567…10
路西法

路西法

不忘初心,方得始终

95 日志
25 分类
59 标签
GitHub E-Mail
© 2019 路西法
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4
本站访客数 人次 本站总访问量 次