打开C++的大门

Posted Ricky_0528

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了打开C++的大门相关的知识,希望对你有一定的参考价值。

初识C++

1.C++的三大特性(背)

①封装

把客观的事务抽象成一个类(将数据和方法打包在一起,加以权限的区分,达到保护并安全使用数据的目的)

②继承

继承所表达的是类之间相关的关系,这种关系使得对象可以继承另外一类对象的特征和能力 -> 避免公用代码的重复开发,减少代码和数据冗余

③多态

多态性可以简单地概括为“一个接口,多种方法”,字面意思为多种形态。程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念

2.C与C++的区别

C面向过程:

核心:功能分解,自顶向下,逐层细化(程序=数据结构+算法)

C++面向对象:

在面向对象中,算法与数据结构被看做是一个整体,称作对象(对象=算法+数据结构)

3.C++命名空间

①:: 作用域运算符

#include<iostream>
int a=100;
int main()
{
    int a=10;
    std::cout<<"局部变量a="<<a<<std::endl;
    std::cout<<"全局变量a="<<::a<<std::endl;//这里::取全局变量
    std::cin.get();
}

②namespace解决命名冲突

a.命名空间只能全局范围内定义

b.命名空间可以嵌套命名空间

c.命名空间是开放的,可以随时把新的成员加入已有的命名空间中

d.命名空间中的函数,可以在命名空间外定义

e.无名命名空间,其命名空间的标识符只能在本文件访问

f.命名空间可以取别名

#include<iostream>
namespace A
{
    int a=100;
    void func(int n);
}
void A::func(int n)
{
    for(int i=0;i<n;i++)
        std::cout<<i<<' '<<std::endl;//命名空间中的函数,可以在命名空间外定义
}
namespace B
{
    int a=10;
    namespace C
    {
        int a=1;//命名空间可以嵌套命名空间
    }
}
namespace A
{
    int b=666;//命名空间是开放的,可以随时把新的成员加入已有的命名空间中
}
int main()
{
    namespace sp=A;//命名空间可以取别名
    std::cout<<"A中a="<<A::a<<std::endl;
    std::cout<<"A中b="<<A::b<<std::endl;
    std::cout<<"B中a="<<B::a<<std::endl;
    std::cout<<"B中C中a="<<B::C::a<<std::endl;
    A::func(10);
    std::cin.get();
}

③using使用命名空间

a.会先从命名空间中寻找变量、函数,找不到再从其他地方找

b.局部变量优先

#include<iostream>
namespace A
{
    int a=10;

}
//using namespace A; //全局使用整个命名空间
int main()
{
    int a=100;
//  using A::a;//using直接使用命名空间内的变量会与局部变量冲突

    using namespace A;
    std::cout<<a;//会优先使用局部变量
    
    std::cin.get();
}

4.C++中几个增强

①语法检查增强

a.变量不能重复定义

b.所有变量与函数必须要有类型

c.不同类型的变量不能直接赋值,需要相对应的强转

②结构体增强

a.结构体中可以定义成员函数

b.创建变量无需加struct关键字

#include<iostream>
struct test
{
    int a=10;
    void Output_num(void)//定义成员函数
    {
        std::cout<<a<<std::endl;
    }
}; 
int main()
{
    test a;//无需struct
    a.Output_num();
    std::cin.get();
}

③新增bool类型

标准C++的bool类型有两种内建的常量true(转换为整数1)和false(转换为整数0)表示状态,它们都为关键字。bool类型只有两个值,占1个字节大小。给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换为false(0)

④三目运算符功能增强

C++中三目运算表达式返回值为变量本身的引用,可以赋值

5.C/C++中的const

C语言中(默认为外部链接)

①const修饰全局变量,变量名只读,内存空间在文字常量区(只读),因此不能通过地址来修改空间内容

②const修饰局部变量,变量名只读,内存空间在栈区(可读可写),可以通过地址间接的修改空间内容

C++中(默认为内部链接)

①对于基础类型,用const修饰编译器会把它放入符号表中

②对其取地址,系统才会给data开辟空间

③若用变量名对const修饰的变量赋初值,系统会为其直接开辟空间

④对于自定义数据类型(结构体、对象等),用const修饰系统会给其开辟空间

⑤若要转换为外部链接,要在定义处加上extern

#include<iostream>
int main()
{
    const int data = 10;//放入符号表
    std::cout << "data = " << data << std::endl;
    int *p = (int *) &data;//取地址系统才会给其开辟空间
    *p = 20;
    std::cout << "*p = " << *p << std::endl;
    std::cout << "data = " << data << std::endl;//原先的值并不会改变
    
    int b = 100;
    const int a = b;//此时a不会放入符号表中,系统会直接为其开辟空间
    std::cout << "a = " << a << std::endl;
    p = (int *)&a;
    *p = 200;
    std::cout << "*p = " << *p << std::endl;
    std::cout << "a = " << a << std::endl;//原先的值也会跟着一起改变
    
    std::cin.get();
}

⭐️尽量用const替换#define

①const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查

②const有作用域,而#define不重视作用域,默认定义处到文件结尾。如果定义在指定作用域下有效的常量,那么#define就不能用

③宏不能作为命名空间的成员,const可以

6.引用(给变量取别名)

引用必须初始化且一旦初始化就不能再修改别名

①作用于数组

#include<iostream>
int main()
{
    //法1
    int arr[5]={1,2,3,4,5};
    int (&my_arr)[5]=arr;//[]优先级高于& 若不加括号则先与[]结合 b为一个数组里面的元素均为引用
    for(int i=0;i<5;i++)
        std::cout<<my_arr[i]<<' ';
    
    //法2
    typedef int TYPE_ARR[5];//TYPE_ARR就是一个数组类型(有5个元素,每个元素为int)
    TYPE_ARR &myArr=arr;//TYPE_ARR=int [5]
    for(int i=0;i<5;i++)
        std::cout<<myArr[i]<<' ';
    std::cin.get();
}

②作为函数参数

#include<iostream>
void Swap(int &a,int &b)
{
    int tmp=a;
    a=b;
    b=tmp;
}
int main()
{
    int x=5,y=10;
    std::cout<<x<<' '<<y<<std::endl;
    Swap(x,y);//直接传值即可
    std::cout<<x<<' '<<y<<std::endl;
    std::cin.get();
}

③作为函数返回值

a.函数返回值为引用时,不要返回局部变量

b.当函数返回值作为左值,函数的返回值类型必须是引用(多用于运算符重载)

#include<iostream>
int& My_data(void)
{
    static int num=200;
    std::cout<<"num = "<<num<<std::endl;
    return num;//函数返回什么变量,引用就是该变量的别名
}
int main()
{
    int &ret=My_data();
    std::cout<<"ret = "<<ret<<std::endl;
    My_data()=2000;//函数返回值作为左值
    My_data();
    std::cin.get();
}

④引用的本质

引用在C++内部是一个指针常量,Type& ref=val == Type* const ref = &val;

⑤指针的引用

#include<iostream>
#include<cstdlib>
#include<cstring>
void MyStr1(char **p_str) //p_str=&str 即 *p_str=str
{//对*p_str操作就相当于对str操作
    *p_str=(char *) calloc(1,sizeof (char));
    strcpy(*p_str,"hello world");
}
void MyStr2(char* &my_str)//&my_str = str2  my_str为str2的一个别名 str2的定义为char* 又&my_str=str2 故形参定义为...
{
    my_str=(char *) calloc(1,sizeof(char));
    strcpy(my_str,"hello world");
}
int main()
{
    //任务:封装一个函数 从堆区给str申请一个空间 并赋值为“hello world”
    char *str1=nullptr;
    MyStr1(&str1);//在函数内部更改指针指向,要传地址进去
    std::cout<<"str1 = "<<str1<<std::endl;
    free(str1);
    char *str2=nullptr;
    MyStr2(str2);
    std::cout<<"str2 = "<<str2<<std::endl;
    free(str2);
    std::cin.get();
}

函数参数变成指针的引用,不用取得指针的地址

⑥常引用

引用传参不会开辟新的空间,常引用在降低开销的同时,也能防止被修改

#include<iostream>
typedef struct
{
    int num;
    char name[10];
}STU;
void PrintStruct(const STU &tmp)//tmp即为常引用
{//这里只有读取操作,故用const修饰,防止被修改
    std::cout<<"学号:"<<tmp.num<<" 姓名:"<<tmp.name<<std::endl;
}
int main()
{
    STU s1={2020135,"Ricky"};
    PrintStruct(s1);
    std::cin.get();
}

常量的引用 应不能被修改 故要用const修饰

const int &num=10;

7. 内联函数(inline function)

保持预处理宏的效率又增加安全性,而且可以像一般成员函数那样可以在类里访问自如

内联函数为了继承宏函数的效率,没有函数调用时的开销。然后又可以像普通函数那样,可以进行参数、返回值类型的安全检查,又可以作为成员函数

是一个真正的函数,函数的替换发生在编译阶段

#include<iostream>
inline int My_mul(int x,int y)
{
    return x*y;
}
int main()
{
    std::cout<<My_mul(10+10,20+20)<<std::endl;
    std::cin.get();
}

⭐️在类内部定义的函数会自动成为内联函数,不需要加inline关键字

内联函数的条件(加inline仅仅是给编译器一个建议):

  • 不能存在任何形式的循环语句
  • 不能存在过多的条件判断语句
  • 函数体不能过于庞大
  • 不能对函数进行取址操作

8.默认(缺省)参数

声明函数原型时可为一个或多个参数指定默认的参数值,当函数调用的时候如果没有传递该参数值,编译器会自动用默认值替代

#include<iostream>
int Add(int x=10,int y=20)
{
    return x+y;
}
int main()
{
    std::cout<<Add(100,200)<<std::endl;
    std::cout<<Add(100)<<std::endl;
    std::cout<<Add()<<std::endl;
    std::cin.get();
}

注意

  • 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数

  • 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认参数,定义处的默认参数时无效的

    建议在函数声明处设置默认参数

9.占位参数

占位参数只有参数类型声明,而没有参数名声明

调用函数时必须给占位参数传参,但不能再函数内使用

10.函数重载(多态的特性)

定义:

同一个函数名在不同场景下可以具有不同的含义

意义:

方便的使用函数名

条件

同一个作用域 参数个数不同 参数类型不同 参数顺序不同

注意

  • 函数的返回值类型不能作为函数重载的依据
  • 函数重载和默认参数一起使用,需要额外注意二义性问题
#include<iostream>
int Add(int x,int y=20)
{
    std::cout<<"int int的Add"<<std::endl;
}
int Add(int x)
{
    std::cout<<"int的Add"<<std::endl;
}
int main()
{
    Add(10);//产生二义性,两个Add都能识别
    std::cin.get();
}

11.C++和C混合编程

fun.h

#ifndef FUN_H
#define FUN_H
#if __cplusplus
extern "C"{
#endif
    int Add(int x,int y);
    int Sub(int x,int y);
#if __cplusplus
};
#endif
#endif //FUN_H

fun.c

#include<stdio.h>
int Add(int x,int y)
{
    return x+y;
}
int Sub(int x,int y)
{
    return x-y;
}

main.cpp

#include<iostream>
#include"fun.h"
int main()
{
    std::cout<<Add(100,200)<<std::endl;
    std::cout<<Sub(100,200)<<std::endl;
    std::cin.get();
}

以上是关于打开C++的大门的主要内容,如果未能解决你的问题,请参考以下文章

C++打开C++的大门

C→C++打开C++世界的大门

有趣的 C++ 代码片段,有啥解释吗? [复制]

以下代码片段 C++ 的说明

C++ 代码片段执行

这些 C++ 代码片段有啥作用?