【C++基础】与C的区别

本文详细介绍了C++相对于C语言新增的一些关键特性,包括动态初始化、bool类型、引用(&)、无类型指针(void*)及其转换规则、作用域限定符、引用类型、从键盘读入字符串、const关键字的用法、枚举、结构和联合类型、函数重载、模板、异常处理等。内容深入浅出,有助于理解C++的语法和编程思想。
摘要由CSDN通过智能技术生成

C++基础

C++相对C新增:

1. 动态初始化

a. 想使用的时候才定义变量

b. for循环中定义变量:for(int i;😉

2. 数据类型

  1. bool :

    a. 相应新增true、false保留字

    b. 输出0和1

  2. class

  3. &(引用)

3. 无类型指针(void *)与显示类型转化

  1. void指针可以指向任意类型的数据,即可以用任意类型的指针对void指针赋值。例如:
int  *pint   ;
void *pvoid  ;      //它没有类型,或者说这个类型不能判断出指向对象的长度
pvoid = pint ;      //只获得变量/对象地址而不获得大小,即pvoid的指针类型还是没有转变
						//但是不允许 pint=pvoid;
printf("%d",*pint);		//正确
printf("%d",*pvoid);	//错误,pvoid不是指向某类型的指针
printf("%d",*(int *)pvoid);   //正确,同printf("%d",*pint);
				   
  1. 在C语言中,可以把任何类型的指针(包括函数指针)赋值给void*指针而无须进行强制类型转换。

    在C++中,对这个转换做了更严格的限制:只有非 常量指针才能赋值给void*指针。
    C++示例代码:

    const char pp[10] = {0};
    const char* str = "aaa";		//pp与str均为指向常量区的指针
    void* pv = NULL;
    pv = pp;//错误,不能将常量指针赋值给void *指针
    pv = str;//错误,不能将常量指针赋值给void *指针
    
    
  2. 如果要将pvoid赋给其他类型指针,则需要强制类型转换如:

    pint = (int *)pvoid;     //转换类型也就是获得指向变量/对象大小
    
  3. void指针在强制转换成具体类型前,不知道指向对象的(字节)大小,不能解引用(即取内容的意思)

    【Eg】 void *pvoid=pint;

    ​ *pvoid //错误;

  4. void指针不能参与指针运算,除非进行转换。

    只知道地址,不知道指针类型(大小),

    即:参与运算的指针必须是确定知道其指向数据类型大小的。

  5. void指针的应用

当进行纯粹的内存操作的时候,或者传递一个指向未定类型的指针时,可以使用void指针;
典型的如内存操作函数memcpy和memset的函数原型分别为:

void * memcpy(void *dest,constvoid * src,size_tlen);

void * memset(void * buffer,intc,size_tnum);

这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。
注意memcpy和memset函数返回的也是void*类型。

【注】:malloc函数的返回类型是void**

int p =(int)malloc(100sizeof(int));

进行了强制性转化。

  1. C++中 int(x)与(int)x意义相同,

    而C中只有(int)x;

4. ::限定符和变量作用域

  1. 作用域小的变量覆盖作用域大的变量
  2. 用“::变量名”可以访问具有文件作用域的变量,但不能访问隐藏的局部变量

【Eg】:变量作用域:

#include <iostream> 
using namespace std; 
 
int x=1; //全局作用域

void func() {
  int x=2; //局部变量,隐藏了全局变量
  cout<<"局部变量"<<x<<endl;
  {
    int x=3; //复合语句中定义的局部变量
    //它隐藏了局部和全局变量
    cout<<"复合语句中定义的局部变量"<<x<<endl;
    cout<<"可以通过::限定符访问全局变量"<<::x<<endl;
    cout<<"但不能访问被隐藏的局部变量"<<endl;
  }
  cout<<"复合语句结束后又可以访问局部变量"<<x<<endl;
  cout<<"的人还是可以通过::限定符访问全局变量"<<::x<<endl;
}

void main() 
{
  cout<<"全局变量"<<x<<endl;
  cout<<"通过::限定符访问全局变量"<<::x<<endl;
  func();
}

/*******************************************
程序输出结果如下:
全局变量1
通过::限定符访问全局变量1
局部变量2
复合语句中定义的局部变量3
可以通过::限定符访问全局变量1
但不能访问被隐藏的局部变量
复合语句结束后又可以访问局部变量2
的人还是可以通过::限定符访问全局变量1
********************************************/

5. 引用类型&

引用本质上是一个指针常量int *const p;(存放的地址不能变,即:不能修改引用的值)

 const int &a=10;	//允许
 int &b=10;		//不允许

在C++中值传递可以实现单向传递,但是引用传递可以实现双向传递。

引用类型

1. 引用(&)是标识符的别名

例如:int i, j ; int & ri = i; //定义int引用ri,并初始化为变量i的引用。
定义一个引用时,必须同时对他进行初始化,使它指向一个已存在的对象。

【PS】:初始化时指向的这个对象只能是变量,不能是常量。

  j = 10 
  ri = j;  //相当于是i=j
  • 一旦一个引用被初始化后,就不能改为指向其他对象
  • 可以取引用的地址

2. 用途:引用可以作为形参代替指针使用

  • 若条件允许,就应将引用形参声明为const形参,这样可以避免无意间修改数据而导致编程错误。

  • 若形参中无const修饰,则引用调用的实际参数只能是变量!(存在合理的地址空间)

形参有用const 修饰,则实参也可以是常量(N,‘A’等)。

即:使用const形参使得函数能够接受const和非const实参,否则只能处理非const数据。

void func1(int n) {  //可以修改n,但值不会返回调用者} //可用值或变量调用
void func2(int &n){  //可以修改n,值会返回调用者} //只能用同类型的变量调用该函数
void func3(const int &n) {  //根本就不允许修改n} //可用值或变量调用

  1. 返回引用类型:
  • 返回引用必须保证返回的引用变量有合法的内存空间,并且不在函数的运行栈中。一般只能返回全局变量和静态变量或者函数传递的形式参数
  1. 不能建立引用数组:

    • 引用是需要初始化的,大数组初始化麻烦

    • 破坏数组存放的连续性

int a[3];
int &b[3]=a; //错误,不能建立引用数组

  1. 不能建立引用的引用,不能建立引用的指针

6. 从键盘读入字符串

char s[80];
cin>>s;	//1.遇到空白停止 2.输入可能超过80个字符
cin.getline(s,80); //最多输入79个字符
cout<<s;

7. const

7.1只读函数

int getMember() const;

表示不能改变对象的成员变量的值,也不能调用一个非const的成员函数(避免间接修改成员数据);

7.2 const与指针
  1. 指针不能指向常量(const 定义的常量)
 #include<iostream>
using namespace std;
int main()
{
	const int N=10;
	int a[N];			//允许。在C++中,N是常量,而C中N是不能改变的变量 
} 
  1. const int *cp==int const *cp;

同为指向常量的指针(const int )**p,且cp可以指向其他普通变量,但是无法通过*cp来修改变量的值。

  1. int * const cp;

指针常量(这个指针是常量),不能指向其他地址,但是可以修改指向的内容。

  1. 在C中,下述语句是错误的:(N是不能改变的变量)
    const int N=10; //必须改为:#define N 10
    int a[N];

    但在C++中,上述语句是允许的。(N是常量)

8. 枚举、结构、联合类型

enum weekday  {sun,mou,tue,wed,thu,fri,sat};
weekday a,b,c; //weekday也是一种类型 仅C++支持)
			//C语言中需要enum weekday a,b,c;

结构与联合同上,可省去struct和union;

8.1枚举类型
void test()

{undefined

enum Week { Mon, Tue, Wed, Thi, Fri, Sat, Sun };

enum Other { One, Two, Three };

enum Week week = Mon;

C中:

  1. 允许非枚举值赋值给枚举类型, 允许其他枚举类型的值赋值给当前枚举类型

    week = 100; //ok

    week = One; //ok

  2. 枚举值具有外层作用域,容易造成名字冲突 int One = 100; //error

  3. 不同类型的枚举值可以直接比较

  4. 可以改变枚举元素的值

C++中:

  1. C++ 只能允许赋值枚举值

    // week = 100; //error

    // week = One; //error

  2. 枚举元素会暴露在外部作用域,不同的两个枚举类型,若含有相同枚举元素,则会冲突

    enum OtherWeek { Mon };

  3. 枚举元素是常量,不能改变

8.2结构

C语言结构体初始化,可以如下四种:

#include <stdio.h>

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

//定义结构体类型
struct Person
{
    char *name;
    int age;
    double heigth;
};

//初始化的4种方式

//1.定义的同时初始化
struct Person p1 = {"zhangsan",20,170};
  
//2.先定义再逐个初始化
struct Person p2;
p2.name = "ykd";
p2.age = 18;
p2.heigth = 180;

//3.先定义再一次性初始化
struct Person p3;
p3 = (struct Person){"lisi",18,180};

//注意:结构体和数组在这里的区别,数组不能先定义再进行一次性初始化
//结构体要明确的告诉系统{}中是一个结构体

//4.指定将数据赋值给指定的属性
struct Person p4 = {.heigth=1.77, .name="wangwu", .age=33};

C++结构体初始化会更加丰富,有内置函数来执行。

当然也可兼容C语言上述四种。除此还有以下两种:

struct ListNode {

int val;
struct ListNode *next;
ListNode(int x) //链表初始化
{
	val = x;
	next = NULL;
}

};
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) 😕/链表初始化
val(x), next(NULL) {}
};

以上都可以采用ListNode a(1),b(2),c(3);直接初始化。

9. 函数

函数声明:

如果你声明一个函数:int fun();

函数参数为空的时候,在c中的意思是函数参数不确定,而在c++中的意思是函数参数为空。

如果函数这样声明:int fun(void);,这时不论是在c中还是在c++中的意思都是函数参数为空。

默认参数
  1. 在函数声明处体现,定义中不能出现
  2. 默认参数只能放形参列表的最后
函数重载
  1. 同名函数才有重载
  2. 根据函数的参数类型,数量,顺序进行重载,返回值不行
//第十一题 (函数重载) 源代码1 

#include <iostream>
using namespace std;
int func(int x)
{
cout<<"int x"<<endl;
}
int func(double x)

{
	cout<<"double x"<<endl;
}
int func(float x)
{
	cout<<"float x"<<endl;
}
int func(int x,double y)

{
	cout<<"int x,double y"<<endl;
}
int func(double x,int y)
{
	cout<<"double x,int y"<<endl;
}
//double func(double x); //声明报错,返回值不能作为唯一区分进行函数重载 

int main() {
	func(1);		//输出int x
	func(1.0);		//输出double x
	func(1.0f);		//输出float x
	func(1,2.0); 	//输出int x,double y
	func(1.0,2);	//输出double x,int y 
	func(10,10); 	//报错, call of overloaded 'func(int, int)' is ambiguous 
}

3.关于函数重载时的自动类型转化

//第十一题 (函数重载) 源代码2 
#include <iostream>
using namespace std;

int func1(double x)					//关于函数重载时的自动类型转化 
{
	cout<<"double x"<<endl;
}

int func2(double x)
{
	cout<<"double x"<<endl;
}

int func2(float x)
{
	cout<<"float x"<<endl;
}

int main() {
	func1(1);	 //输出double x,进行了自动类型转化
	func1(1.0f); //输出double x,进行了自动类型转化
  	func2(1.0f);	//输出float x;
	func2(1);	 //同时存在double和float类型参数的函数,编译器不知道如何转化,报错 
}

内联函数

//第九题 (内联函数) 源代码1 

#include <iostream>

using namespace std;

inline int add(int x,int y)//内联函数的声明和定义,inline加在定义处才有用 

{ 
return x+y; 
} 


inline void Foo(int x,int y); 

int main() 

{
}

void Foo (int x,int y)//仅在声明处出现inline,不构成内联函数 

{

}

10.模板

  1. 函数模板参数表中指明的类型参数必须用于函数参数表,例如:

    template T f1() {……} //错误

    template void f2()
    { //错误

    ​ T a;

    ​ ……

    }

  2. 模板函数的声明和定义必须放在同一个cpp下,否则连接器无法通过模板函数的声明找到相对应的定义 (模板未被实例化的时候是没有生成二进制代码的)

11.抛出异常:

  1. 抛出异常类时,调用构造函数 (要加括号) //或者直接抛出该类的对象
#include <iostream>
using namespace std;

void func(void);

class Expt { //异常类
public:
  Expt() {cout<<"执行异常类的构造函数...\n";}
  Expt(const Expt &e) {
    *this=e;
    cout<<"执行异常类的拷贝构造函数...\n";
  }
  ~Expt() {cout<<"执行异常类的析构函数...\n";}
  const char *showReason() const {
    return "Expt类异常!\n";
  }
};

class Demo {
public:
  Demo()  {cout<<"构造Demo...\n";}
  ~Demo() {cout<<"析构Demo...\n";}
};

void func(void) {
  Demo dObj;
  cout<<"在函数func()中抛出Expt类异常!\n";
  throw Expt(); //不能写成:throw Expt;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值