3. c++特性

cpp对c的扩展

前面的部分是C++基础,和C基础完全一样;C++完全兼容C

面向对象编程

1. 面向过程

以过程为中心,分析出解决问题需要的步骤,用函数一步步实现。核心是:功能分解,自顶向下,逐层细化

2. 面向对象(Object-Oriented Programming, OOP)

将实体看作一个个对象,每个对象有自己的属性(变量)和操作(方法)

封装 把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。 类将成员变量和成员函数封装在类的内部,根据需要设置访问权限,通过成员函数管理内部状态。
继承 继承所表达的是类之间相关的关系,这种关系使得对象可以继承另外一类对象的特征和能力。 继承的作用:避免公用代码的重复开发,减少代码和数据冗余。
多态 多态性可以简单地概括为“一个接口,多种方法”,字面意思为多种形态。程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念

作用域运算符 ::

:: 解决归属问题

例如有一个全局变量a,又有

int a = 10;
int main(void){
    int a = 1;
    cout<<a<<endl;//输出:1
    cout<<::a<<endl;//输出:10
    return 0;
}

命名空间 namespace

C语言中可以通过static关键字,使得名字只在本编译单元内可见;C++可以通过控制命名空间来实现

避免大型项目中的命名冲突

1. 创建命名空间

namespace A{
	int a = 10;
}
namespace B{
	int a = 20;
}
void test(){
	cout << "A::a : " << A::a << endl;//10
	cout << "B::a : " << B::a << endl;//20
}

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

不能定义在函数内部

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

namespace A{
    int a = 10;
    namespace B{
        int a = 20;
    }
}
void test(){
    cout<<A::a<<endl;//输出:10
    cout<<A::B::a<<endl;//输出:20
}

4. 命名空间是开放的,可以随时添加新成员

//相同命名空间会自动合并
namespace A{
    int a = 10;
}
namespace A{
    void func(){
        cout<<"merge namespace"<<endl;
    }
}

5. 声明和实现可以分离

namespace space{
    void func1();
    void func2(int data);
}
//在外部实现时,一定要加上空间名字
void space::func1(){
    return;
}
void space::func2(int data){
    return;
}

6. 无名命名空间

//表示只能在当前文件内访问
namespace{
    int a = 10;
    void func(){return;}
}

7. 命名空间别名

namespace veryLongName{
    int a = 10;
    void func(){return;}
}
void test(){
    namespace shortName = veryLongName;
}
//可以使用veryLongName::a,也可以使用shortName::a去访问命名空间

using声明命名空间的成员

1. 使用

namespace A{
    int paramA = 20;
    int paramB = 30;
    void funcA(){ cout << "hello funcA" << endl; }
    void funcB(){ cout << "hello funcA" << endl; }
}
void test(){
    //1. 通过命名空间域运算符(推荐)
    cout << A::paramA << endl;
    A::funcA();
    //2. using声明成员可用
    using A::paramA;
    using A::funcA;
    cout << paramA << endl;
    //cout << paramB << endl; //不可直接访问
    funcA();
    //3. 同名冲突
    //int paramA = 20; //相同作用域注意同名冲突
}

2. using 声明成员碰到函数重载

namespace A{
    void func(){}
    void func(int x){}
    int func(int x,int y){}
}
void test(){
    using A::func;
    func();
    func(10);
    func(10, 20);
}

如果命名空间包含一组用相同名字重载的函数,using声明就声明了这个重载函数的所有集合。

3. using声明整个命名空间可用

namespace A{
    int num = 10;
}
void test(){
    using namespace A;//声明整个命名空间
    cout<<num;
    
    int num = 20;
    cout<<num;//不会发生冲突,先在局部变量中找,找不到再去命名空间中找
}
namespace A{
    int num = 10;
}
namespace B{
    int num = 10;
}
void test(){
    using namespace A;
    using namespace B;
    cout<<num<<endl;//报错,无法识别是A的还是B的
}

struct类型增强

C定义结构体时需要加上struct关键字,且不能定义函数

C++不用加struct关键字,能够定义函数

bool类型关键字

bool flag = true;//false,占1个字节

引用

在C和C++中,指针使用是一样的,但是C++增加了另外一种给函数传递地址的途径,这就是按引用传递

变量名实质上是一段连续内存空间的别名,引用可以作为一个已定义变量的别名

1. 引用的定义

void test(){
    int a = 10;
    
 	int &b = a;//给a取别名,必须初始化
}
// & 修饰变量为引用,b就是a的别名。系统不会为引用开辟空间,操作a等价于操作b
// a=b; &a=&b;

2. 数组的引用

(1)&myArr 设置别名

(2)int arr[5] 定义引用目标

(3)int (&myArr)[5] 整体替换

int arr[5]={10,20,30,40,50};
int n = sizeof(arr)/sizeof(arr[0]);
int (&myArr)[5] = arr;
int i=0;
for(i=0;i<n;i++){
	cout<<myArr[i]<<" ";//10 20 30 40 50
}
cout<<endl;

3. 指针变量的引用

int num = 10;
int *p = &num;
int * &my = p;
cout<<*p<<endl;//输出:10
cout<<*my<<endl;//输出:10

4. 函数的引用

void func1(void){
    cout<<"func1"<<endl;
}
void (&fu)(void) = func1;
fu();

5. 引用作为函数的参数

函数内部可以通过引用操作外部变量

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

int main(void){
    int a = 10, b = 20;
    swap(a, b);
    cout<<a<<endl;//输出:20
    cout<<b<<endl;//输出:10

    return 0;
}

6. 引用作为返回值

(1)不要返回局部变量的引用

局部变量在函数结束后就释放,返回给主函数后会访问到一个未定义变量

(2)返回值类型为引用,可以完成链式操作

struct Stu{
    Stu &printStu(Stu &ob, int value){
        cout<<value<<endl;
        return ob;
    }
};
int main(void){
    Stu ob1;
    ob1.printStu(ob1, 100).printStu(ob1, 200).printStu(ob1, 300);//100 200 300
}

7. 常引用

(1)给常量取别名,不能通过常引用修改内容

//int &a = 10;//报错,左边是int类型,右边是const int类型
const int &a = 10;//不能修改a的值
//a = 100;//报错

(2)常引用作为函数的参数:防止函数内部修改外部的值

void printInt(const int &a){
    a = 200;//报错
    cout<<a<<endl;
}
int main(void){
    int num = 100;
    printInt(num);
}

内联函数

使用 inline 修饰,必须在定义时使用,不能在声明时使用

int func(int x, int y);//声明
inline int func(int x, int y){
    return x+y;
}

内联函数:在编译阶段,将内联函数中的函数体 替换到 函数调用处。避免函数调用时的开销。

宏函数和内联函数的区别:

宏函数和内联函数都会在适当的位置进行展开,避免函数调用开销。
宏函数在预处理阶段展开,内联函数在编译阶段展开
宏函数的参数没有类型,不能保证参数的完整性;内联函数的参数有类型 能保证参数的完整性。
宏函数没有作用域的限制,不能作为命名空间、结构体、类的成员;内联函数有作用域的限制,能作为命名空间、结构体、类的成员

注意事项:

在内联函数定义的时候加inline修饰
类中的成员函数 默认都是内联函数(不加inline 也是内联函数)
加上inline不一定是内联函数(内联函数条件),不加inline也可能是内联函数
不能存在任何形式的循环语句,不能存在过多的条件判断语句
函数体不能过于庞大,不能对函数取地址

函数重载

函数的参数类型、个数、顺序不同可以作为重载,但单纯返回值不同不能作为重载;

void print(int a);
void print(int a, int b);//这就是重载,根据传入的参数选择具体哪一个函数执行

关于返回值不能做重载的原因

int ret = func();//调用参数时,才能根据int ret确定返回值类型,单纯执行func()时,无法识别
func();//不接返回值时,就无法知道是哪个函数了,因此被禁止

函数重载的底层实现原理

void func(){};
void func(int x){};
void func(int x, char y){};
//上面三个函数在linux下生成的编译之后的函数名为(不同编译器可能产生不同的内部名)
_24funcv	//v表示void
_24funci	//i表示int
_24funcic	//c表示char

函数的默认参数

C++在声明函数原型时,可以设置默认值,没有传入时,会采用默认值

void test01(int a = 10, int b = 20){
    cout<<a+b<<endl;
}
//注意:假设有参数a b c d,那么如果b设置了默认参数,那么后面的所有参数都需要设置默认参数
void test02(int a, int b = 10, int c = 20, int d = 30){}

默认参数和函数重载同时出现一定要注意二义性

void func(int x){};
void func(int x, int y=10){};
int main(void){
    func(10,20);//可以
    func(10);//产生二义性,10可以传给x,然后y采用默认的10
}

占位参数

C++在声明函数时,可以设置占位参数,只有参数类型,没有参数名。一般情况,在函数内部无法使用占位参数

void TestFunc01(int a,int b,int){
    //函数内部无法使用占位参数
    cout << "a + b = " << a + b << endl;
}
//占位参数也可以设置默认值
void TestFunc02(int a, int b, int = 20){
    //函数内部依旧无法使用占位参数,但还是用不了
    cout << "a + b = " << a + b << endl;
}
int main(){
    //错误调用,占位参数也是参数,必须传参数
    //TestFunc01(10,20);
    //正确调用
    TestFunc01(10,20,30);
    //正确调用
    TestFunc02(10,20);
    //正确调用
    TestFunc02(10, 20, 30);
    return 0;
}

extern “C”

由于重载,C语言和C++对同一函数的编译名称不一样。如果用C++去编译C语言,则会出现找不到函数的情况,链接错误。
extern “C” 就是使编译器按照C语言的方式去编译和链接

//需要在头文件中声明
#ifndef TESTC_H
#define TESTC_H
#include <stdio.h>
#if __cplusplus//识别C++工程,如果是C++,则执行extern,否则不用
extern "C"{
#endif
    extern void func1();
    extern int func2(int a, int b);
#if __cplusplus
}
#endif
#endif

示例:

main.cpp

#include <iostream>
#include "fun.h"
using namespace std;

int main(void){
    cout<<myAdd(100, 200)<<endl;
    return 0;
}

fun.c

#include <stdio.h>
#include "fun.h"

int myAdd(int a, int b){
    return a+b;
}

(1)未加extern “C”

fun.h

#ifndef FUN_H
#define FUN_H

extern int myAdd(int a, int b);

#endif // FUN_H

报错,提示找不到myAdd函数

(2)加上extern “C”

fun.h

#ifndef FUN_H
#define FUN_H

#if __cplusplus
extern "C"{
#endif
    extern int myAdd(int a, int b);
#if __cplusplus
}
#endif
#endif // FUN_H
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值