一. 默认参数
函数参数出现缺省参数,位于后面的参数缺省参数必须已被声明
void func1(int, int, int = 10);
void func1(int, int = 6, int);
void func1(int = 4, int, int);
void func1(int a, int b, int c)
{
cout << a << " " << b << " " << c << endl;
}
void func2(int a = 4, int b = 6)
{
cout << a << " " << b << endl;
}
int main()
{
cout << "---begin---" << endl;
func1();
func2();
cout << "---end---" << endl;
return 0;
}
输出结果:
---begin---
4 6 10
4 6
---end---
这种书写方式同样可以应用于类的成员函数中,在类内声明时缺省靠后的参数,在类外实现时缺省靠前的参数
class Test
{
public:
Test(int, int = 4);
};
Test::Test(int a = 3, int b)
{
cout << a << " " << b << endl;
}
int main()
{
cout << "---begin---" << endl;
Test();
cout << "---end---" << endl;
return 0;
}
输出结果:
---begin---
3 4
---end---
如果类外的默认参数会使非默认构造函数变为默认构造函数,则程序非良构,应尽量避免这种写法
class Test
{
public:
Test(int); // 非默认构造
};
Test::Test(int a = 3){} // 缺省后变为默认构造
同时,默认参数应避免类内类外重定义
class Test
{
public:
Test(int = 4);
};
Test::Test(int a = 3) {}
当类实现多态时,子类覆盖函数的默认参数会根据对象的静态类型确定
class Animal
{
public:
virtual void func(int a = 1)
{
cout << "Animal : " << a << endl;
}
};
class Cat : public Animal
{
public:
void func(int a = 3) override
{
cout << "Cat : " << a << endl;
}
};
int main()
{
cout << "---begin---" << endl;
unique_ptr<Animal> ptr1{new Cat()};
ptr1->func();
unique_ptr<Cat> ptr2{new Cat()};
ptr2->func();
cout << "---end---" << endl;
return 0;
}
输出结果:
---begin---
Cat : 1
Cat : 3
---end---
对程序进行编译分析时所得到的的表达式类型称为表达式的静态类型,程序执行过程中静态类型将不会改变,在上面的实例中ptr1的静态类型为Animal,动态类型为Cat,而ptr2的静态类型与动态类型都为Cat,故两者调用func时的默认参数不相同。
函数的缺省值不能通过非静态成员变量进行赋值,但静态成员变量可以
class Test
{
const int n = 3;
static int t;
public:
// Test(int a = n); 无法通过非静态成员变量赋值
Test(int a = t)
{
cout << a << endl;
}
};
int Test::t = 4;
int main()
{
cout << "---begin---" << endl;
Test();
cout << "---end---" << endl;
return 0;
}
输出结果:
---begin---
4
---end---
二、成员指针
数据成员指针或虚函数成员指针不会指向一个内存,而是存储在该类中的偏移地址
成员指针会指向特定的内存,但不能脱离实例对象使用
成员函数指针在声明时需要加上作用域,由实例对象进行调用
class Test
{
public:
void func(int a)
{
cout << a << endl;
}
};
int main()
{
cout << "---begin---" << endl;
void (Test::*print)(int) = &Test::func;
Test test, *test_ptr = &test;
(test.*print)(5);
(test_ptr->*print)(6);
cout << "---end---" << endl;
return 0;
}
输出结果:
---begin---
5
6
---end---
同样,函数指针也可以作为函数参数
class Test
{
public:
void func(int a)
{
cout << a << endl;
}
};
void run_func(void (Test::*p)(int), Test &test)
{
(test.*p)(1);
}
int main()
{
cout << "---begin---" << endl;
Test test;
run_func(&Test::func, test);
cout << "---end---" << endl;
return 0;
}
当函数指针作为函数参数时,若存在多个匹配的重载函数时,应使用static_cast来制定调用的函数类型
class Test
{
public:
void func()
{
cout << "void" << endl;
}
void func(int a)
{
cout << a << endl;
}
};
void run_func(void (Test::*p)(int), Test &test)
{
(test.*p)(1);
}
void run_func(void (Test::*p)(void), Test &test)
{
(test.*p)();
}
int main()
{
cout << "---begin---" << endl;
Test test;
// run_func(&Test::func, test); 存在多个匹配的对象,无法确定调用哪一个
run_func(static_cast<void (Test::*)(void)>(&Test::func), test);
run_func(static_cast<void (Test::*)(int)>(&Test::func), test);
cout << "---end---" << endl;
return 0;
}
在C++17之后,我们可以使用<functional.h>库中的invoke直接对成员变量与对象进行绑定:当传入数据成员指针时,将会得到一个与对象绑定的引用变量;当传入函数成员指针时,将会对特定对象直接调用该函数
class Test
{
public:
int a = 1;
void func()
{
cout << "void" << endl;
}
};
void run_func(void (Test::*p)(int), Test &test)
{
(test.*p)(1);
}
int main()
{
cout << "---begin---" << endl;
Test test;
int &num = invoke(&Test::a, &test);
num++;
cout << test.a << endl;
invoke(&Test::func, test);
cout << "---end---" << endl;
return 0;
}
输出结果:
---begin---
2
void
---end---