c++可变参数

在java中,可变参数使用 int … 需要声明类型
在c++中,可变参数直接使用 … 表示,不限制类型,而是在取值时,申明类型
在c++,获取可变参数使用 va_list va_start va_arg va_end

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 sum(int count, ...) {
va_list vp; // 可变参数的动作
va_start(vp, count); // 第二个参数:内部需要一个 存储地址用的参考值,如果没有第二个参数,内部他无法处理存放参数信息

int num1 = va_arg(vp, int); // 取值
cout << "num1: " << num1 << endl;

num1 = va_arg(vp, int); // 取值
cout << num1 << endl;

num1 = va_arg(vp, int); // 取值
cout << num1 << endl;

num1 = va_arg(vp, int); // 取值
cout << num1 << endl;

// 关闭
va_end(vp);
}

int main() {
cout << "Hello, World!" << endl;
sum(666, 8, 19, 20);
return 0;
}

关于 va_start 中第二个参数的解释:

  • 可变参数获取其实是根据内存地址来获取的,将函数传入的可变参数前的一个参数作为 va_start 的第二个参数,就是获取到前一个参数的地址,就可以获取到可变参数的首地址,然后再根据取值的类型,既可获取存储需要的size,就可以获取到每一个可变参数的值 *

注意事项:

  • 取值可变参数多个值时,需要用同一个变量接收,即使用同一个内存地址接收
  • 当取的值超出了可变参数的长度时,并不会报错,而是取到一个系统内存地址的值,随机
  • 当取值完毕要调用 va_end 来结束可变参数的获取

我们在使用可变参数时,必须传递一个具体的参数,用于确定可变参数的内存地址,

  • 同时为了方便使用,我们可以传递可变参数的长度。这样也就不会存在可变参数越界取到随机值的问题
1
2
3
4
5
6
7
8
9
10
11
12
void sum(int count, ...) {
va_list vp;
va_start(vp, count);
int num = 0;
int i = 0;
for(i = 0; i < count; i ++) {
num = va_arg(vp, int);
cout << num << endl;
}
va_end(vp);
}
sum(3, 8, 19, 20);

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
42
43
44
45
46
47
48
49
50
51
52
53
54
class Person {

public:
int age;
static int id; // statin int id = 99; 会报错 Non-const static data member must be initialized out of line
Person(){
id = 99; // 编译不通过
}
static void update(){
id = 9; // 编译不通过
}
void change(){
id = 87; // 编译不通过
}
};

int Person::id= 88; // 加上这一句,这样就可以了。
int main() {
cout << "Hello, World!" << endl;
Person person;
person.change();
Person::update(); // 类名::可以调用静态函数
cout << person.id << endl; // id没实现也会报错

return 0;
}

```c++
静态的总结:
* 1.可以直接通过类名::静态成员(字段/函数)
* 2.静态的属性必须要初始化,然后再实现(规则)
* 3.静态的函数只能取操作静态的属性和方法(Java)

## this

this 指当前的对象

** const修饰函数的this意义何在。 **
之前讲过常量指针、指针常量、常量指针常量
使用const 修饰的函数其实就是对该函数 内部隐式的 this 进行const 修饰,使当前this变为一个 常量指针常量

* 常量指针 常量是修饰值的,不能修改指,可以修改地址
* 指针常量,不可修改指向的地址,但可以修改指针指向的值
* 常量指针常量 既不能修改指向的地址,也不能修改指针指向的数据,即完全只读

也就是说使用const修饰的函数,既不能修改 当前对象 中属性的值,也不能修改地址


```c++
void test() const {
this->age = 19; // 报错 Cannot assign to non-static data member within const member function 'test'
int num = 88;
&this = NULL; // 报错 Cannot take the address of an rvalue of type 'const Person *'
}

友元函数

常规来说,private的变量在类之外,都是无法访问的,即使是在外部生成的变量或者子类都是无法修改的。
但是在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
class Person {
private:
int age;
public:
void setAge(int age) {
this -> age = age;
}
int getAge() {
return this->age;
}
// 申明友元函数
friend void updateAge(Person *person, int age);
};
// 友元函数的实现
void updateAge(Person *person, int age) {
person->age = age;
}

int main() {
cout << "HELLO WoRLD!" << endl;
Person person;
// person.age = 10; // 会报错 'age' is a private member of 'Person'
person.setAge(19);
updateAge(&person, 88);
cout << "age: " << person.getAge() << endl;
return 0;
}

友元类

除了友元函数,还存在友元类,和友元类类似,在友元类中,类对象可以访问私有属性与私有函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Student {
private:
int age;
friend class Class; // 友元类

};

class Class {
public:
Student student;
void setStudentAge(int age) {
student.age = age;
};
int getStudentAge() {
return student.age;
};
};
Class cla;
cla.setStudentAge(18);
cout << "友元类中输出私有属性:" << cla.getStudentAge() << endl;

运算符重载

重写运算符的逻辑,使用关键字 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
class Point{
private:
int x,y;
public:
Point(int x, int y) : x(x), y(y) {

}
int getX(){
return this->x;
}
int getY(){
return this->y;
}
void setX(int x){
this->x = x;
}
void setY(int y){
this->y = y;
}
};
// 对+号做运算符重载
Point operator + (Point point1, Point point2) {
int x = point1.getX() + point2.getX();
int y = point1.getY() + point2.getY();
Point point(x, y);
return point;
}
// 对象1 + 对象2 C++默认不支持的, Java也不支持,Kotlin也不支持

// C++/Kotlin 运算符重载 + 把+重载掉

int main() {
cout << "HELLO WORLD!" << endl;
Point point1(100, 100);
Point point2(200, 200);
Point point = point1 + point2;
cout << "运算符+重载 point x:" << point.getX() << " y: " << point.getY() << endl;
// 这里的加号还是可以正常运算
int a = 1 + 2;
cout << "1 + 2 = " << a << 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
59
60
61
class Point{
private:
int x,y;
public:
Point(int x, int y) : x(x), y(y) {

}
int getX(){
return this->x;
}
int getY(){
return this->y;
}
void setX(int x){
this->x = x;
}
void setY(int y){
this->y = y;
}

// 对+号重载
// 系统是这样写的 常量引用:不允许修改,只读模式
// const 关键字的解释
// & 性能的提高,如果没有& 运行+ 构建新的副本,会浪费性能
// 如果增加了& 引用是给这块内存空间取一个别名而已
Point operator + (const Point & point) {
int x = this->x + point.x;
int y = this->y + point.y;
return Point(x, y);
}
// 对-号重载
Point operator -(const Point & point) {
int x = this->x - point.x;
int y = this->y - point.y;
return Point(x, y);
}
// 对 ++对象 进行重载
void operator ++ () {
cout << "++对象" << endl;
this->x = this->x + 1;
this->y = this->y + 1;
}
// 对 对象++ 进行重载
void operator ++(int) {
cout << "对象++" << endl;
this->x = this->x + 1;
this->y = this->y + 1;
}
};

int main() {
cout << "HELLO WORLD!" << endl;
Point point1(100, 100);
Point point2(200, 200);
Point point = point1 + point2;
cout << "运算符+重载 point x:" << point.getX() << " y: " << point.getY() << endl;
point1++;
++point1;
cout << "运算符++重载 point1 x:" << point1.getX() << " y: " << point1.getY() << endl;
return 0;
}

注意 ++对象 与 对象++ 的区别