入门c++学习

1.命名空间域——namespace

——>简单功能介绍:

       我们会有时会遇到以下代码:

#include<stdio.h>
int a = 0;
namespace num
{
  int a = 1;
}
int main()
{
  int a = 2; 
  printf("%d\n", a);
 return 0;
}

   这些代码当然是正确的,三个整形的名字相同也可以吗?因为他们在不同的范围内,所以当然是可以的,在一个范围内名字相同,这是不可以的。

     我们如果运行程序会发现,输出的是0,可以看出是有一定的顺序的——访问一个变量,顺序是局部域->全局域->展开了的命名空间域或者指定访问的命名空间域。在图中代码中,num相当于是一个命名空间域的名字,namspace则是命名空间域的关键字。

    对于上面说到的展开的命名空间域,演示如下:

在namespace num前面加上using后,相当于打开了这个命名空间域,那么这时候再去printf会发现一个问题,这个a发生了错误,那么我们可以得到展开了的命名空间域就类似于原本空间域里面的a暴露在了和全局变量一样的全局域里面。所以不要轻易的使用using。展开命名空间指的是编译时编译器是否会到里面去搜索。

   这个关键字使用来解决命名冲突的问题的,给出以下代码:

#include<stdio.h>
#include<stdlib.h>

int rand = 0;

int main()
{
 printf("%d\n",rand);
 return 0;
}

乍一看下面的代码没有什么问题但是运行的时候就会报错,这是因为rand和stdlib头文件里面的一个函数名重合了,所以会发生这样的错误。正确的方式避免这种情况就是使用namespace。

把这个变量放在一个域里面,然后用::来找到它,::就是所谓的域作用限定域。::在这个符号左边加上命名空间域的名字,就可以找到里面的变量,如果左边是空白的没加东西,那么就会找到名字为a的全局变量

 ——>命名空间的定义:

    (1) 命名空间内可以定义变量,函数和类型等。

    (2)命名空间可以嵌套使用。

    (3)同一个工程中允许存在多个相同名称的命名空间,编译器最后会合并为同一空间。
      

      注:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都受限于该命名空间中。

——>命名空间的使用:

命名空间有三种使用方法:

   (1)加命名空间域名称以及作用域限定符:

int main()
{
   printf("%d\n", N::a );
   return 0;
}

这样就可以正确的使用到N这个命名域里面的a变量了。

   (2)使用using namespace来引入变量:

namespace N1
{
int a;
int b;
int Add(int left, int right)
 {
     return left + right;
}

using namespace N;

int main()
{
  printf("%d\n", b);
  return 0;
}

打开这个作用域后就可以直接使用了,但是在一些大型项目里面是不建议这样使用的。最好的方式应该是下一种:

  (3)使用using将命名空间中某个成员引入:

using N::b;
int main()
{ 
  printf("%d\n", b);
  return 0;
}

这样在打代码的过程中就可以把只把常用的东西给“放出来”。

2.c++的输出和输入

——>书写hello world:

  我应该如何用c++写一个hello world呢:

#include<iostream>
//std是c++标准库的命名空间名,c++将标准库的定义实现都放在这个命名空间里面。
using namespace std;
int main()
{
  cout<<"hello world!"<<end1;
  return 0;
}

说明:

(1) 使用cout标准输出对象(控制台)cin标准输入对象(键盘)时,必须包含< iostream >头文件
以及按命名空间使用方法使用std
(2)cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<
iostream >头文件中。
(3) <<是流插入运算符,>>是流提取运算符
(4)使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。
C++的输入输出可以自动识别变量类型。
(5) 实际上coutcin分别是ostreamistream类型的对象,>><<也涉及运算符重载等知识。
注意:实际上在用cout输出有多位小数的数字时,他有时候会省略几位小数,这时候我们可以c语言和c++混合使用。
  • 先包含头文件再使用 using namespace std;:这是常见的做法。先包含头文件,编译器能知道有哪些标识符可用,接着使用 using namespace std; 让这些标识符在当前作用域直接使用。
  • 先使用 using namespace std; 再包含头文件:这种顺序也可行。不过在包含头文件之前,由于没有引入具体的标识符,using namespace std; 暂时没有实际作用。只有在包含头文件之后,其中的标识符才会被引入到当前作用域。

——>std的使用惯例:

(1) 在日常练习中,建议直接using namespace std即可,这样就很方便。
(2) using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对
象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模
大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 +
using std::cout展开常用的库对象/类型等方式。

3.缺省值

——>缺省值的概念:

缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实
参则采用该形参的缺省值,否则使用指定的实参。
#include<iostream>
using namespace std;
void fun(int a = 0)
{
  cout<<a<<endl;
}

int main()
{
  fun();//使用缺省值
  fun(10);//使用给定的值
  return 0;
}

上述代码就是缺省值的一个具体使用案例,缺省值就相当于预先给定好的一个值。

——>缺省值的类型:

(1)全缺省值:

void add(int a = 5, int b = 10, int c = 20)
{ 
  cout<<a<<endl;
  cout<<b<<endl;
  cout<<c<<end1;
}

 如上面代码所示全缺省值就相当于所有的形参都给上了预定的值。

(2)半缺省值:

void add(int a, int b = 10, int c = 20)
{ 
  cout<<a<<endl;
  cout<<b<<endl;
  cout<<c<<end1;
}

在上述代码中,形式参数a并没有给定预定的参数,这种形式就叫做半缺省值。

注意:(1)在全缺省类型中,你要调用add函数,如果你以此输入了自己想要的两个数字,那么就会按照顺序从左到右覆盖缺省值去使用。中间不能隔着给数字,没有这种定义。

          (2)在半缺省中,所给定的数字是从右往左给出,不能隔着给。

——>使用时的注意事项:

      如果在多文件中使用缺省值的话,只需要在函数的声明的时候给出缺省值。如果在声明和定义使用缺省值的话会报错。

// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该
用那个缺省值。
举例一下:
    Stack.h文件:
//Stack.h文件
#include<iostream>

typedef struct stack
{
  int* a;
  int top;
  int capacity;
}stack;

void stackinit(stack* pst ,int defaultcapacity = 40);

   stack.cpp文件:

#include"Stack.h"


void stackinit(stack* pst,int defaultcapacity)
{
  pst->a = (int*)malloc(sizeof(int)*defaultcapacity);
  if(a == NULL)
   {
     perror("malloc fail");
     return;
   }
 pst->top = 0;
 pst->capacity = defaultcapacity;
}

   在函数声明时给出缺省值就行了,在定义时不需要写出缺省值的大小。

4.函数重载:

函数重载:是函数的一种特殊情况,c++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或类型或参数顺序不同)。常用来处理实现功能类似数据类型不同的函数。

#include<iostream>
using namespace std;

// 1、参数类型不同
int Add(int left, int right)
{
	return left + right;
}
double Add(double left, double right)
{
	return left + right;
}
int main() {
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 3.3) << endl;
	return 0;
	}

5.引用:

引用的概念,就像当于取别名,别名和原本的名字指向同一块空间,我们学习的时候就当做引用时不开辟空间的。

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。
                                 用法:       类型& 引用变量名(对象名) = 引用实体;
   
       
int a = 10;
int& b = a;
int& c = a;
int& d = b;

在上述代码中,bcd均是a的别名,改变bcd的值会改变a

注意:(1)引用类型必须和引用实体同种类型的。

           (2)引用在定义时必须初始化

          (3)一个变量可以有多个引用

          (4)引用一旦引用一个实体,就不可以再引用其他实体了。

(1)引用做参数:

           使用场景,可以在函数里面使用:假设我们要写一个交换函数。

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

     我们使用引用,就不必要和指针一样麻烦,可以直接对对象进行操作。

(2)引用作为返回值:

下面以add函数为例:

int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

注意:引用的使用会涉及权限问题

      (1)权限放大:

const int a = 0;
int& b = a;

    通俗一点的解释就是变量a不能修改,但是b作为a的别名,它却有了可以更改的权力。

     (2)权限可以缩小或者平移:

int x = 0;
int& y = x;
const int& z = x;

  原本的变量可以修改,别名不能修改原本的值,这就是一种权限的缩小。

    (3)以下代码是值得注意的:

double d = 1.11;
int c = d;
int& r = d;

上述代码在编译器里面输入会发现是明显错误的,上述的代码存在隐式转换。用int类型的r去作为一个double类型的别名,会出现一个d的int类型的临时变量,转换过程都存在这种变量,这种临时变量具有常性,所以r存在权限放大的问题,是不对的,这段代码的最后一行变为const int& r = d;

就是一个正确的代码。

    (4)在函数使用中的问题:

int func()
{
  static x = 0;
  return x;
}

int main()
{
 int& ret = func();
}

在函数 返回时,返回的是一个和(3)中一样具有常性的临时变量,所以这里存在和(3)中一样的问题。

常规写法和使用引用的对比举例:

    常规顺序表(静态):

#include <stdio.h>
#include <assert.h>

typedef struct SeqList
{
    int a[100];
    int size;
} SeqList;

// 找到pos位置的值是什么
int sltfind(SeqList* ps, int pos)
{
    assert(ps != NULL);
    return ps->a[pos];
}
// 修改pos位置的值,变成x
void sltmodify(SeqList* ps, int pos, int x)
{
    assert(pos < 100 && ps != NULL);
    ps->a[pos] = x;
}

int main()
{
   
    return 0;
}

     使用引用改进这段顺序表:

#include<iostream>
#include<cassert>

typedef struct SeqList
{
 int a[100];
 int size;
}SeqList;

//找到pos位置的值
int& SLAt(SeqList* ps,int pos)
{
  assert(pos>=0 && pos<ps->size && ps!=NULL);
  return ps->a[pos];
}

int main()
{
   SeqList sl;
    sl.size = 3;
    sl.a[0] = 1;
    sl.a[1] = 2;
    sl.a[2] = 3;

    // 测试获取指定位置的值
    int value = SLAt(&sl, 1);
    std::cout << "位置 1 的值是: " << value << std::endl;

    // 测试修改指定位置的值
    SLAt(&sl, 1) = 10;
    std::cout << "修改后位置 1 的值是: " << sl.a[1] << std::endl;

    return 0;
 return 0;
}

总结:使用引用做返回值,我们会发现不仅减少了拷贝,还可以修改返回值+获取返回值

6.内联函数:

概念:以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调 用建立栈帧的开销,内联函数提升程序运行的效率。
使用方法:
        在函数的声明前加上inline即可。

——>内联函数的特点:

(1). inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
(2). inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。

(3)inline函数不建议声明和定义分开,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接的时候会找不到。

----->一个经典题目:

       宏的优缺点?
    优点:
       1.增强代码的复用性。
       2.提高性能。
    缺点:
     1.不方便调试宏。(因为预编译阶段进行了替换)
     2.导致代码可读性差,可维护性差,容易误用。
     3.没有类型安全的检查 。
     C++有哪些技术替代宏
     1. 常量定义 换用const enum
     2. 短小函数定义 换用内联函数
【无人机】基于改进粒子群算法的无人机路径规划研究[和遗传算法、粒子群算法进行比较](Matlab代码实现)内容概要:本文围绕基于改进粒子群算法的无人机路径规划展开研究,重点探讨了在复杂环境中利用改进粒子群算法(PSO)实现无人机三维路径规划的方法,并将其与遗传算法(GA)、标准粒子群算法等传统优化算法进行对比分析。研究内容涵盖路径规划的多目标优化、避障策略、航路点约束以及算法收敛性和寻优能力的评估,所有实验均通过Matlab代码实现,提供了完整的仿真验证流程。文章还提到了多种智能优化算法在无人机路径规划中的应用比较,突出了改进PSO在收敛速度和全局寻优方面的优势。; 适合人群:具备一定Matlab编程基础和优化算法知识的研究生、科研人员及从事无人机路径规划、智能优化算法研究的相关技术人员。; 使用场景及目标:①用于无人机在复杂地形或动态环境下的三维路径规划仿真研究;②比较不同智能优化算法(如PSO、GA、蚁群算法、RRT等)在路径规划中的性能差异;③为多目标优化问题提供算法选型和改进思路。; 阅读建议:建议读者结合文中提供的Matlab代码进行实践操作,重点关注算法的参数设置、适应度函数设计及路径约束处理方式,同时可参考文中提到的多种算法对比思路,拓展到其他智能优化算法的研究与改进中。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值