【C++程序设计】从c走进c++

1.函数指针

    (1)基本概念:程序运行期间,每个函数都会占用一段连续的内存空间。而函数名就是该函数所占内存区域的起始位置(也称入口地址)。我们可以将一个函数的入口地址赋值给一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以调用这个函数。这种指向函数的指针被称为函数指针。

    (2)函数指针定义形式:

        类型名(*指针变量名)(参数类型1,参数类型2,....)

        例如:int(*pf)(int,char);         表示pf是一个函数指针,它所指向的函数返回值应该是int,该函数应有两个参数,第一个是Int类型,第二个是char类型。

    (3)使用方法:可以用一个原型匹配的函数的名字给一个函数指针赋值。要通过函数指针调用它所指向的函数,写法为:

            函数指针名(实参表);

            例:

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
void PrinMin(int a, int b) {
	if (a < b)
		cout << a << endl;
	else
		cout << b << endl;
}
int main() {
	void(*pf)(int, int);
	int x = 4, y = 5;
	pf = PrinMin;
	pf(x, y);
	getchar();
	return 0;
}

    (4)函数指针和qsort库

        void qsort(void*base,int nelem,snsigned int width,int(*pfCompare)(const void*,const void*));

        可以对任意类型的数组进行排序

        对数组排序,需要知道:

        (1)数组起始地址

        (2)数组元素个数

        (3)每个元素的大小(由此可以算出每个元素的地址)

        (4)元素谁在前谁在后的规则

 

        base:待排序数组的起始地址,nelem:待排序数组元素个数,width:待排序数组的每个元素的大小(以字节为单位)

        pfCompare:比较函数的地址。比较函数是自己编写的。

        排序就是一个不断比较并交换位置的过程。qsort函数在执行期间,会通过pfCompare指针调用“比较函数”,调用时将要比较的两个元素的地址传给“比较函数”,然后根据“比较函数”返回值判断两个元素哪个更应该排在前面。

        比较函数编写规则:int 比较函数名(const void*elem1,const void* elem2)

        a.如果*elem1应该排在*elem2前面,则函数返回值是负整数。

        b.如果*elem1和*elem2哪个排在前面都行,那么函数返回0

        c.如果*elem1应该排在*elem2后面,则函数返回值是正整数

        例程:将数字按个位数从小到大排序

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int MyCompare(const void*elem1, const void*elem2)
{
	unsigned int*p1;
	unsigned int*p2;
	p1 = (unsigned int*)elem1;
	p2 = (unsigned int*)elem2;
	return *p1 % 10 - *p2 % 10;
}

#define NUM 5
int main()
{
	unsigned int a[NUM] = { 5,9,13,14,28 };
	qsort(a, NUM, sizeof(unsigned int), MyCompare);
	for (int i = 0; i < NUM; i++)
		cout << a[i]<<endl;
	system("pause");
	getchar();
	return 0;
}

 

2.命令行参数

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

    {

        ........

    }

    argc:代表启动程序时,命令行参数的个数。C/C++语言规定,可执行程序程序本身的文件名,也算一个命令行参数,因此argc的值至少是1.

    argv:指针数组,其中每一个元素都是一个char*类型的指针,该指针指向一个字符串,这个字符串里就存放着命令行参数。

            例如,argv[0]指向的字符串就是第一个命令行参数,即可执行程序的文件名,argv[1]指向第二个命令行参数,argv[2]指向第三个命令行参数。

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
int main(int argc, char*argv[])
{
	for (int i = 0; i < argc; i++)
		cout << argv[i] << endl;
	return 0;
}

    将上面的程序编译成sample.exe,然后在控制台窗口敲:

    sample para1 pare2 s.txt5 "hello world"

 

 

3.位运算

    有时候需要对某个整数类型变量中的某一位进行操作,例如,判断某一位是否为1或只改变其中某一位而保持其他位都不变。C++语言提供了6种位运算符来进行“位运算”操作:按位与(&)、按位或(|)、按位异或(^)、取反(~)、左移(<<)、右移(>>)。

     

4.引用的概念和应用

    (1)引用定义的方式如下:

        类型名&引用名=同类型的变量名。

    某个变量的引用和这个变量是一回事,相当于该变量的一个别名。注意:

        1.定义引用时一定要将其初始化,否则编译不会通过。通常会用某个变量去初始化引用。

        2.初始化后,它就一直引用该变量,不会再引用其他变量了。也可以用一个引用去初始化另一个引用,这样两个引用就引用同一个变量了。

        3.引用只能引用变量,不能用常量初始化引用。

 

    (2)引用的应用

    在C语言中,编写两个整形变量值交换的函数;

void swap(int a,int b)
{ int tmp;
  tmp=a;a=b;b=tmp
}
int n1,n2;
swap(n1,n2)

    在C语言中,这样写变量的值不会被交换,因为函数里改变形参的值不会影响到实参。

    C语言中正确的写法:

    

void swap(int* a,int* b)
{ int tmp;
  tmp=*a;*a=*b;*b=tmp
}
int n1,n2;
swap(&n1,&n2)

    C++里,可以应用引用:

 

void swap(int& a,int& b)
{ int tmp;
  tmp=a;a=b;b=tmp
}
int n1,n2;
swap(n1,n2)

    这样写变量的值会改变,因为int& a表示引用,引用后两个变量是等价的,a等价于n1,b等价于n2,改变a的值n1也会跟着变。

    (3)常引用

    定义引用时,可以在前面加“const”关键字,则该引用就成为“常引用”。

    如: int n;

            const int&r=n;

    常引用和普通引用的区别在于不能通过常引用去修改其引用的内容。注意,不是常引用所引用的内容不能被修改,而是不能通过常引用修改其引用的内容。如:

    int n=100;

    const  int&r=n;

    r=200;                //编译出错,不能通过常引用修改其引用的内容

    n=300;                //没问题,n的值变为300.

        注意:const T&和T&是不同的类型。

        (1)T&类型的引用或T类型和变量可以用来初始化const T&类型的引用。

        (2)const& T类型的常变量和const T&类型的引用则不能用来初始化T&类型的引用,除非进行强制类型转换。

 

5.const关键字的用法

    (1)定义常量。定义变量时如果在类型名前面加上“const”关键字,该变量就变为“常变量”

        const int a=5.

        常变量的值只能用初始化的方式给出,此后不能被修改。对常变量赋值会导致编译出错。

 

    (2)定义指针。定义指针的时候,可以在前面加const关键字,则该指针就成为“常量指针”。

        const T* P;

            常量指针和普通指针的区别在于:不能通过常量指针去修改其指向的内容。注意,不是常量指针所指向的内容不能被修改,只是不能通过常量指针修改其指向的内容,可以用别的办法修改。   

const int* p;
int n=100;
p=&n;
*p=200;        //编译出错,不能通过常量指针修改其指向的内容。
n=300;         //编译正确,n的值变为300


        注意:const T*和T*是不同的类型。T*类型的指针可以赋值给const T*类型的指针,反过来不行,除非进行强制类型转换。

 

        

 

        函数参数为常量指针时,可避免函数内部不小心改变函数参数指针所指地方的内容。

 

6.动态内存分配

   【1】. C++里可以用new运算符实现动态内存分配,使得程序可以在运行期间,根据实际需要,要求操作系统临时分配给自己一片内存空间用于存放数据。此种内存分配是在程序运行中运行的,而不是在编译时就确定的,因此称为“动态内存分配”。new运算符的两种用法如下:

    (1)P=new T;

        其中,T是任意类型名,P是类型为T*的指针。动态分配出一片大小为sizeof(T)字节的内存空间,并且将该内存空间的起始地址赋值给P。例如:

        int *P;

        p=new int;

        *p=5;

    (2)P= new T[N]

        其中,T是任意类型名;P是类型为T*的指针;N代表“元素个数”,可以是任何值为正整数的表达式,表达式中可以包含变量、函数调用等。这样的语句动态分配出N*sizeof(T)个字节的内存空间,这片空间的起始地址被赋值给P。例如:

        int *pn;

        int i=5;

        pn=new int[i*20]

        pn[0]=20;

        pn[100]=30'

        最后一行编译时没问题,但运行时会导致数组越界。

    注意:new 运算符返回值的类型:new T ,new T[N] 返回值的类型都是T*。

 

   【2】C++里用delete运算符释放动态分配的内存,new出来的动态内存空间一定要用delete运算符进行释放。用法:

       (1) delete 指针;  //该指针必须是指向动态分配的内存空间的,否则运行时很可能会出错。例如:

        int *p=new int;

        *p=5;

        delete p;

        delete p;    //本句会导致程序出错 因为p指向的空间已经释放了,p不再是指向动态分配的内存空间的指针了。

        (2) delete[ ]指针;

           例如:

           int *p=new int[20];

           p[0]=1;

            delete[ ]p;

            同样要求,被释放的指针p必须是指向动态分配的内存空间的指针,否则会出错。

            !注意:如果动态分配了一个数组,但是却用“delete指针”的方式释放,没有用[ ],则编译时没有问题,运行时也一般不会发生错误,但实际上会导致动态分配的数组没有被完全释放。

 

7.内联函数

    函数调用是有时间开销的,如果函数内部本身就没有几条语句,执行的时间本来就很短,而且函数被反复执行很多次,相比之下,调用函数所产生的开销就比较大。

    为了减少函数调用的开销,引入了内联函数机制,编译器处理对内联函数的调用语句时,是将整个函数的代码插入到调用语句处,而不会产生调用函数的语句。

    内联函数的写法:在定义函数时,在返回值类型前面加上“inline”关键字。例如:

        inline int Max(int a,int b)

        {

                if(a>b)

                    return a;

                  return b;

        }

        内联函数将整个函数体的代码插入调用语句处,就像整个函数体在调用处被重写了一遍一样。内联函数比使用普通函数会使最终可执行程序的体积增加。以时间换取空间或增加空间消耗来节省时间,也是计算机学科中常用的办法。

        另外需要注意的是,调用内联函数的语句前面,必须已经出现内联函数的定义(即整个函数体),而不能只出现内联函数的声明。

 

8.函数重载

        一个或多个函数名字相同,然而参数个数或参数类型不相同,这叫做函数的重载。C语言里没有函数重载机制,但C++有。

        以下三个函数是重载的关系:

                int Max(double f1,double f2){}    

 

                int Max(int n1,int n2){}

                int Max(int n1,int n2,int n3){}

        函数重载使函数命名变得简单。编译器根据调用函数中的实参个数和类型判断应该调用哪个函数。

 

        注意:同名函数只有参数不同才算重载,如果两个同名函数参数表相同而返回值类型不同,不是重载而是重复定义,是补允许的。

 

9.函数的缺省参数

    C++中,定义函数的时候可以让最右边的连续若干个参数有缺省值,那么调用函数的时候,若相应位置不写参数,参数就是缺省值。

    如:

        void func(int x1,int x2=10,int x3=8){

            func(10);  //等效于func(10,10,8)

            func(10,8);//等效于func(10,8,8)

            func(10,,10); //不行,只能最右边的连续若干个参数缺省。

    缺省参数优点:

        (1)编写函数调用语句时可以少输入参数,尤其在函数参数个数多时能省事儿。

        (2)使程序的可扩充性变好,即程序需要增加新功能时改动尽可能少。如果某个写好的函数需要添加新的参数,而原先那些调用该函数的语句,未必需要使用新增的参数,为了避免那些对原先写好的调用语句的修改,就可以使用缺省参数。

 

 

 

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值