当前位置:网站首页 / C++ / 正文

AY的VS2019 的C++11笔记[5] -函数

时间:2020年07月09日 | 作者 : aaronyang | 分类 : C++ | 浏览: 1010次 | 评论 0

函数

发现C++的函数,只能定义在被调用的上方,不然找不到,即时在下面定义都不行。

image.png

这样可以的。

bb方法在下面就不行

image.png

只列出我觉得和C#不一样的地方


例如它的 局部静态对象

size_t count_calls() {
	static size_t ctr = 0;
	return ++ctr;
}

//int id = 122;
int main()
{
	for (size_t i = 0; i < 10; i++)
	{
		cout << count_calls() << endl;
	}
	
	return 0;
	}

输出1到10


C#函数内不允许static的




函数声明(函数原型)

这玩意有点类似C#的 委托声明

也像C#接口的定义的方法

 void print(int a,string b);

image.png

上面声明一下,然后就可以调用,具体bb()方法的内容,可以定义在被调用的 代码 之下了。



分离式编译

允许我们把程序分割到几个文件中去,每个文件独立编译

具体百度。

因为这个敲命令行的,我还不太会。

如果我们修改了其中一个源文件,那么只需重新编译那个改动了的文件。大多数编译器提供了分离式编译每个文件的机制,这一过程通常会产生一个后缀名是 .obj(Windows) 或 .o(UNIX)的文件,后缀名的含义是该文件包含对象代码。


$ CC -c factMain.cc #generates factMain.o

$ CC -c fact.cc #generates fact.o

$ CC factMain.o fact.o # generates factMain.exe or a.out

$ CC factMain.o fact.o -o main # generates main or main.exe



指针形参

image.png

类似C#的ref


传引用参数

void resetRef(int &i) {
	i = 0;
}

image.png



使用引用避免拷贝

比如比较字符串谁更短

bool IsShorter(const string& s1, const string& s2) {
	return s1.size() < s2.size();
}

image.png

当函数无须修改引用形参的值时最好使用常量引用。


函数返回多值

你可以新建新的数据类型个,2个字段,还有简洁的用法,就是类似C#的out用法,我们直接传引用参数,就是上面讲的,形参前面加一个&符号

size_t find(const string &s,char c,size_t &sizet){

 这里对sizet赋值就好了。

}

C#也有Tuple类型,可以,js现在也有了。


C#有方法重载,对于C++的参数,如果被const修饰了,例如下面不算重载,会错误的

void fcn(const int i)

void fcn(int i)


尽量使用常量引用

string::size_type find_char(string& s, char c, string::size_type& occurs) {


}

第一个形参的类型,应该是const string&

下面调用讲在编译时发生错误

image.png


还有二次引用

image.png

上面只读的,传给 普通的string& s,肯定报错


换成这种就好了

image.png




数组形参

在C#直接参数,写个List<T> p1,这种作为方法参数就行了

C++中,要传指针,数组不允许拷贝的


我们使用收尾元素的指针

void print(const int* beg, const int* end) {
	while (beg != end) {
		cout << *beg++ << endl;
	}
}

int main()
{

	int aa[] = { 1,2,3,4,5 };
	print(begin(aa), end(aa));

image.png


还有一种,感觉类似C#

image.png

通用也可以加const修饰,当不需要写的时候



数组的形参引用

void print(int (&array)[10]){

 for(auto ele:array){

cout<<ele<<endl;

}

}

缺点,传递的数组要 10个长度的。




传参多维数组

image.png




 AYUI       www.ayjs.net      AY         杨洋原创编写,请不要转载谢谢

main:处理命令行选项

image.png


目前我们的main都是空的


int main(int argc, char **argv)//也可以写成char *argv[],argc是个数,argv每个选项是一个字符串

注意:argv[0]是命令,不是命令行选项


#include<stdio.h>

#include<stdlib.h>

int main(int argc,char* argv[])

{

    printf("argc:%d\n",argc);

    for(int i = 0;i < argc; ++i)

    printf("%s\n",argv[i]);

    system("pause");

}


argc包括命令行选项的个数

argv包含argc个C风格字符串,代表了由空格分隔的命令选项。例如,对于如下命令行:prog -d -o ofile data0,

argc被设置为5,且argv被设置为下列C风格字符串:

argv[0] = "prog";

argv[1] = "-d";

argv[2] = "-o";

argv[3] = "ofile";

argv[4] = "data0";

argv[5] = 0;


argv[0]总是被设置为当前正在被调用的命令(程序运行生成的exe文件名,不包括扩展名)

从索引1到argc的值,这里是5,所以1到5的值,1表示被传递给命令的实际选项。



命令行编译程序的具体步骤:

先编译运行上述代码,在将生成的exe文件拷贝至C盘目录下(自己设置)。然后打开命令提示符:

输入cd C:\切换工作目录至C盘目录。

再输入prog h wh wh。



program_name [-d] [-h] [-v] [-o output_file] [-l limit_value]  file_name [file_name [file_name [...]]]。

//C/C++处理main()函数命令行
#include <iostream>
#include <vector>
#include <string>
#include <ctype.h>//调用atoi函数
using namespace std;

const char* const prog_name = "prog";
const char* const prog_version = "version 1.0 (2020/7/10)";

//退出函数
inline void usage(int exit_value = 0){
	cerr<<prog_name<<"  usage!"<<endl;
	exit(exit_value);
}

int main(int argc,char* argv[]){
	//声明用于记录用户指定选项的变量
	bool debug_on = false;
	bool ofile_on = false;
	bool limit_on = false;

	string ofile_name;//记录出现的输出文件名
	int limit = -1;//限制值
	vector <string> file_names;//记录文件名

	cout<<"argc:"<<argc<<endl;
	for(int i = 1;i < argc; ++i){//读取argv中的每个选项
		//输出第i+1个参量
		cout<<"argv["<<i<<"]:"<<argv[i]<<endl;

		char *pchar = argv[i];
		switch(pchar[0]){//确定选项类型:-h,-d,-v,-l,-o;或者其他
			case '-':{
				cout<<"case \'-\' found"<<endl;
				switch(pchar[1]){//确定用户指定的选项:h,d,v,l,o
					case 'd'://处理调试:
						cout<<"-d found:debugging turned on!"<<endl;
						debug_on = true;
						break;
					case 'v'://处理版本请求
						cout<<"-v found:version info displayed!"<<endl;
						cout<<prog_name<<":"<<prog_version<<endl;
						return 0;
					case 'h'://处理帮助
						cout<<"-h found:help info!"<<endl;
						usage();
					case 'o'://处理输出文件
						cout<<"-o found:output file!"<<endl;
						ofile_on = true;
						break;
					case 'l'://处理限制量
						cout<<"-l found:resorce limit!"<<endl;
						limit_on = true;
						break;
					default://无法识别的选项
						cerr<<prog_name<<":error:unrecognition option -:"<<pchar<<endl;
						usage(-1);
				}
				break;
			}
			default://不以'-'开头,是文件名
				if(ofile_on){//输出文件名
					cout<<"file name:"<<pchar<<endl;
					ofile_name = pchar;
					ofile_on = false;//复位
				}
				else if(limit_on){//限制值
					limit_on = false;
					limit = atoi(pchar);
					if(limit<0){
						cerr<<prog_name<<":error:negative value for limit!"<<endl;
						usage(-2);
					}
				}
				else{//文件名
					file_names.push_back(pchar);
				}
				break;
		}
	}
	if(file_names.empty()){
		cerr<<prog_name<<":error:no file for processing!"<<endl;
		usage(3);
	}
	else{
		cout<<(file_names.size() == 1 ? "File":"Files")<<
			" to be processed are the followed:"<<endl;
		for(int i = 0;i < file_names.size();++i){
			cout<<file_names[i]<<"\t"<<endl;
		}
	}
	if(limit != -1){
		cout<<"user-specified limit:"<<limit<<endl;
	}
	if(!ofile_name.empty()){
		cout<<"user-specified ofile:"<<ofile_name<<endl;
	}
	
}


含有可变形参的函数

initializer_list<T>

跟vector差不多,这个语法类似C#的方法参数的params修饰符号

就是不知道有多少个参数


用法

initalizer_list<T> lst;

initalizer_list<T> lst{a,b,c,...};

lst2(lst)拷贝或赋值一个initializer_list对象不会拷贝列表的元素;拷贝后,原始列表和副本共享元素

lst.size();

lst.begin()

lst.end()

image.png

void error_msg(initializer_list<string> li) {
	for (auto begin = li.begin
		(); begin !=li.end(); ++begin)
	{
		cout << *begin << endl;
	}
}


int main()
{
	string expect = "hellay";
	string actual = "helloay";
	if (expect != actual) {
		error_msg({"funcAy",expect,actual});
	}
	else {
		error_msg({ "funcAb","一致的" });
	}


当然error_msg的list参数前面还是可以加其他参数的


C++还有省略符形参

void foo ( parm_list , ...); 

void foo ( ... ); 

#include <cstdarg>
void ArgFunc(const char* str, ...)
{
	va_list ap;
	int n = 3;
	char* s = NULL;
	int d = 0;
	double f = 0.0;

	va_start(ap, str); // 注意!这里第二个参数是本函数的第一个形参 

	s = va_arg(ap, char*);

	d = va_arg(ap, int);

	f = va_arg(ap, double); // 浮点最好用double类型,而不要用float类型;否则数据会有问题 

	va_end(ap);

	printf("%s is %s %d, %f", str, s, d, f);
}

image.png

使用时候

	ArgFunc("The answer", "Hello", 345, 788.234);


省略符形参是为了便于C++程序访问某些特殊的C代码而设置的,这些代码使用了名为varargs的C标准库功能。你的C编译器文档会描述如何使用varargs



方法返回值,有的是复杂的指针,那就在方法名前面加& 或者*号修饰

或者有的用const修饰的


函数完成后,它所占用的存储空间也随之被释放掉。因此,函数终止意味着局部变量的引用将指向不再有效的内存区域。

char& get_val(string &str,string::size_type ix) {
	return str[ix];
}

image.png

返回是对一个字符的引用。



还可以这样写

vector的返回,可以花括号,这个有点像C#的Tuple了

image.png



main函数不能调用自己的。返回0表示成功,其他是失败




返回数组

一般返回数组的指针,或者引用

数组的名字太长了,可以如下简化,arrT是int[10]的别名

image.png


func(int i)表示调用函数时,需要一个int类型的参数;

(*func(int i))表示对调用func的结果执行解引用的操作;

(*func(int i))[10]表示解引用之后得到一个维度为10的数组;

int (*func(int i))[10]表示数组的数据类型为int;


C++ 11 简化,尾置返回类型

auto func(int i) ->int(*)[10];

返回一个指针,该指针指向含有10个整数的数组。


具体更多,参考

//返回指向数组的指针
auto func1(int arr[][3], int n) -> int(*)[3] {
    return &arr[n];
}

要不然你得

int (* func1(int arr[][3], int n))[3] {
    return &arr[n];
}

还有更复杂的

//函数接受一个指向func1函数的指针的参数,返回指向func2的函数的指针;
auto func3(int(*(*ptf)(int arr[][3], int n))[3]) -> int (*(*(*)())(int arr[][3], int n))[3]{
    ...
}


你也可以使用decltype()

例如

int odd[] = { 1,3,5,7,9 };
int even[] = { 0,2,4,6,8 };
decltype(odd)* arrptr(int i) {
	return (i % 2) ? &odd : &even;
}

image.png




函数重载

方法返回值和名字一样,方法参数列表不一样。

const修饰的参数的 方法,参数是指针或者引用时, const在前面算,在后不算。


C++的方法内,可以定义方法

会覆盖外面的同名的方法域


方法的参数也可以默认值,和C#一样



内联函数:

方法前面返回值 类型  前面加一个 inline

inline const string& shortString(const string &s1,const string &s2){


}

constexpr函数

方法前面,或者 字段前面加constexpr,就可以运行时候,计算得到个结果,并把值替换掉 原来位置的代码。


和其他函数一样,内联函数和constexpr函数可以在程序中多次定义。



调试帮助

assert定义在cassert头文件中

预处理宏

assert(word.size()>threshold)


NDEBUG预处理变量

assert依赖一个名为NDEBUG的预处理变量的状态。定义了,assert什么也不做。默认情况没有定义的,运行时检查

具体百度吧

#define NDEBUG


#ifndef NDEBUG

......

#endif


__func__函数的名字

__LINE__当前行号

__FILE__存放文件名

__TIME__

__DATE__




实战下:

新建test2.cpp,从项目中移除test1.cpp

image.png

#include<iostream>
#include<string>
#include<vector>
#include<cassert>
using namespace std;
void read_vi(vector<int>::const_iterator iterator_begin, vector<int>::const_iterator iterator_end) {
#ifndef NDEBUG
	cerr << iterator_end - iterator_begin << __func__ << " " << __FILE__ << " "
		<< __LINE__ << " " << __TIME__ << " " << __DATE__ << endl;
#endif // !NDEBUG
	if (iterator_begin != iterator_end) {
		cout << *iterator_begin << " ";
		return read_vi(++iterator_begin, iterator_end);
	}
	else {
		cout << endl;
		return;
	}
}
int main()
{
	vector<int> v{ 1,2,3,4,5 };
	read_vi(v.begin(), v.end());
	system("pause");
	return 0;
}

如果没有定义NDEBUG

则执行

 AYUI       www.ayjs.net      AY         杨洋原创编写,请不要转载谢谢

代码


下面的main就是 遍历vector 列表

image.png

接下来,打开属性

image.png


image.png

image.png

image.png

点击确定

运行时候,由于定义了,所以不会进入那段代码了

image.png











函数指针

指向是函数而非对象


定义一个函数

bool lengthCompare(const string &,const string &)

定义个函数指针

bool (*pf)(const string &,const string &)



声明了以后

image.png

这个就和C#的委托很像了


调用

image.png

也可以直接

bool bb3 = lengthCompare("hello1", "hello2");


pf=0的话或者pf=nullptr, 意思是,不指向任何函数


当函数指针 指向的 函数有重载

需要

bool (*pf)(const string &,const string &) =lengthCompare

就是返回类型,形参列表要一致


函数指针可以当做 方法的参数,把函数传入,会自动转换为指针

void A1(const string &s1, bool (*pf)(const string&, const string&)) {

}
void A2(const string& s1, bool lengthCompare(const string&, const string&)) {

}

image.png

这样方法的参数 名字类型好长

image.png


函数指针名称冗长,使用typedef和decltype

typedef bool Func(const string&, const string&);
typedef decltype(lengthCompare) Func2;//等价的类型

typedef bool (*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2;//等价的类型


定义类型后,使用

image.png

void UserBigger(const string&, const string&, Func);
void UserBigger(const string&, const string&, FuncP2);



返回指向函数的指针

using F = int(int*, int); //p是函数类型,不是指针

using pF = int(*)(int*, int);//pf是指针类型

image.png



根据需要返回一个函数指针,使用auto和decltype简写

image.png


输入a,返回sum1,输入2返回sum2

string::size_type sum1(const string&, const string&);
string::size_type sum2(const string&, const string&);
decltype(sum1)* getSum(const string&);

string::size_type sum1(const string& str1, const string& str2) {
	return 1;
}
string::size_type sum2(const string& str1, const string& str2) {
	return 2;
}

decltype(sum1)* getSum(const string& str1) {
	if (str1 == "a") {
		return sum1;
	}
	else {
		return sum2;
	}
}

调用如下:

	cout << getSum("a")("a1","b1") << endl;

输出1,换其他,返回sum2,然后传参,返回2




 AYUI       www.ayjs.net      AY         杨洋原创编写,请不要转载谢谢

int demo(int* p, int a) {
	return 12;
}
int (*ff(int x))(int*, int) {
	cout << x << endl;
	return demo;
}
auto ff2(int x) -> int(*)(int*, int) {
	cout << "ay:"<<x << endl;
	return demo;
}

int main()
{
	int a = 8;
	cout << ff2(2)(&a, a) << endl;


image.png



使用别名简写

image.png

int demo(int* p, int a) {
	return 12;
}
int (*ff(int x))(int*, int) {
	cout << x << endl;
	return demo;
}
auto ff2(int x) -> int(*)(int*, int) {
	cout << "ay:" << x << endl;
	return demo;
}

using PF2 = int(*)(int*, int);//pf2是指针类型

PF2 ff3(int x) {
	cout << "ay3:" << x << endl;
	return demo;
}





交换位置

#include<iostream>
using namespace std;
void swap(int *x, int *y) {
	int temp = *x;
	*x = *y;
	*y = temp;
}

int main() {
	int x, y;
	cin >> x >> y;
	cout << x << y << endl;
	swap(x, y);
	cout << x << y << endl;
	system("pause");
	return 0;
}

发现image.png

&号可写可不写的。

感觉写正规点

void swap(int &x, int &y) {
	int temp = x;
	x = y;
	y = temp;
}






 AYUI       www.ayjs.net      AY         杨洋原创编写,请不要转载谢谢




加减乘除,放入vector函数指针类型的列表里

#include<iostream>
#include<string>
#include<vector>
using namespace std;
int add(int a, int b) {
	return a + b;
}
int sub(int a, int b) {
	return a - b;
}
int multi(int a, int b) {
	return a * b;
}
int divide(int a, int b) {
	return a / b;
}
int main()
{	
	vector<int(*)(int, int)> vf{ add,sub,multi,divide };
	for (const auto e : vf) cout << e(4, 2) << endl;
	system("pause");
	return 0;
}


推荐您阅读更多有关于“C++,”的文章

猜你喜欢

额 本文暂时没人评论 来添加一个吧

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

  查看权限

抖音:wpfui 工作wpf,目前主maui

招聘合肥一枚WPF工程师,跟我一个开发组,10-15K,欢迎打扰

目前在合肥市企迈科技就职

AYUI8全源码 Github地址:前往获取

杨洋(AaronYang简称AY,安徽六安人)AY唯一QQ:875556003和AY交流

高中学历,2010年开始web开发,2015年1月17日开始学习WPF

声明:AYUI7个人与商用免费,源码可购买。部分DEMO不免费

不是从我处购买的ayui7源码,我不提供任何技术服务,如果你举报从哪里买的,我可以帮你转正为我的客户,并送demo

查看捐赠

AYUI7.X MVC教程 更新如下:

第一课 第二课 程序加密教程

标签列表