C++基础知识(一)

目录

1. 命名空间  ::

2. using 声明

3. 结构体加强

(1)不需要加struct就可以定义变量

(2)结构体内可以写函数

4. 不能进行隐式转换,必须是显式转换

5. 三目运算符

6. C/C++中的const

7. 引用(重难点)

(1)引用可以作为一个已定义变量的别名

(2)当定义函数时,按引用传递

(3)建立数组引用

(4)引用的本质

(5)指针引用

(6)常量引用

(7)引用的使用

① 引用作为函数的参数

② 引用作为函数的返回值

8. 内联函数

9. 函数的默认参数

10. 函数的占位参数

11. 函数传参的三种方式

12. 创建一个学生类


1. 命名空间  ::

namespace A{   //A 是空间的名字
    int a;
    void func()
    {}
}

(1)命名空间只能写在全局;

(2)可以嵌套;

namespace Aspace {  
    int a = 1;
    namespace B {
        int b;
    }
}

(3)命名空间是开放的,可以随时加入新成员;

(4)命名空间可以取别名;

namespace newSpace = oldSpace;
newSpace:新名字 
oldSpace:旧名字

(5)分文件编写代码时,如果.h中有两个命名空间,但是里边的成员函数或变量重名时,在.cpp中实现函数时需要加上命名空间。eg:

test.h:

#pragma once
#include <iostream>
using namespace std;

namespace Space1 
{
    void func();
}

namespace Space2
{
    void func();
}

test.c:

#include "test.h"
void Space1 :: func()  //需要在函数名之前加上命名空间的名字
{
    cout << "func" << endl;
}

(6)作用域运算符 ::

eg:

#include <iostream>
using namespace std;  //标准命名空间

namespace Aspace {   
    int aa = 1;
    void func()
    {
    }
    namespace B {
        int b = 3;
    }
}

namespace Cspace
{
    int c = 2 ;
}


int aa = 100;

int main()
{
    int aa = 10;
    cout << "aa =" << aa << endl;        //就近原则,打印局部变量aa的值
    cout << "::aa =" << ::aa << endl;    //如果::前边没有东西,则表示取全局的作用域,打印全局变量的aa
    cout << "Cspace::c =" << Cspace::c << endl;   //::前边加上命名空间的名字则打印命名空间中对应成员的值
    cout << "Aspace::B::b =" << Aspace::B::b << endl;

    system("pause");
    return EXIT_SUCCESS;
}

输出为:

aa =10
::aa =100
Cspace::c =2
Aspace::B::b =3

2. using 声明

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

namespace A {
	int a = 1;
	int b = 2;
	int c = 3;
}

void test01()
{
	//using声明是让命名空间中某个标识符可以直接使用
	using A::a;
	cout << a << endl;

	int a = 50; //错误,因为using声明了某个变量,在该作用域内不能定义同名的变量
}

void test02()
{
	//using 编译指令,让某个命名空间中的标识符都可以直接使用
	using namespace A;
	cout << a << endl;

	int a = 10; //这里没错,因为这里类似于命名空间中的a为全局变量,而这个a是局部变量
}

3. 结构体加强

(1)不需要加struct就可以定义变量

struct Maker
{
	char name[64];
	int age;
};

void test()
{
    Maker a;
}

(2)结构体内可以写函数

struct Maker2
{
	int a;
	void func()
	{
		cout << "func" << endl;
	}
};

void test()
{
    Maker2 a2;
    a2.func();
}

4. 不能进行隐式转换,必须是显式转换

void test()
{
	char* p = (char*)malloc(64);
}

5. 三目运算符

(1)C语言中,三目运算符返回的是右值,不能被赋值;

void test()
{
	int a = 10;
	int b = 20;
    printf("%d",a > b ? a : b);
	//(a > b ? a : b) = 100;  //err
}

但是,如果想改变三目运算符结果的值,则可以这样子写:

 *(a > b ? &a : &b) = 100; 

(2)C++中,三目运算符返回的是左值,是空间,可以被赋值;

void test()
{
	int a = 10;
	int b = 20;
	(a > b ? a : b) = 100;  
    cout<<"a ="<<a<<endl;
    cout<<"b ="<<b<<endl;
}

输出为:

a=10;
b=100;

左值和右值的概念:
左值:Lvalue,代表location,表示内存可以寻址,可以赋值;

右值:Rvalue,代表read,表示可以知道它的值。 

6. C/C++中的const

CC++
const修饰的全局变量会被存储在常量区,只要有定义即分配内存空间,不能修改,也不能间接修改全局const当声明extern或者对变量取地址时,编译器才会分配存储地址,变量存储在只读数据段
const修饰的局部变量存储在堆栈区,不能直接改变变量的值,可以通过指针间接修改const的值

只有在分配了内存后(此时编译器不会进行优化),才能通过指针间接修改const变量的值。

① 对于const int a = 10这种,编译器会进行优化,将a替换为10,类似于#define;(如果不想让编译器进行优化,可以在定义变量时加上volatile)

② 如果用一个变量初始化const,比如const int a = b,那么也会给a分配内存;

③ 对于类对象,也会分配内存,如下代码所示。

默认为外部连接,当两个c文件中都有同样的const变量时,编译器会报重定义错误;使用别的文件的全局const需要加extern。默认为内部连接,只能在本文件使用,重名的const变量不会报重定义错误。如果想让C++中的const有外部链接,必须在定义时就加上extern,如:extern const int a = 10;

(1) 对于基础数据类型,也就是const int a = 10;这种,编译器会进行优化,直接用值替换。

const int a = 10;
int* p = (int*)&a;
*p = 20;
cout << "a = "<< a << endl; //编译器在编译阶段把这里优化了,等价于:cout<<"a="<<10<<endl;
cout << "*p = "<< *p << endl;

输出为:

a = 10
*p = 20

(2)对于基础数据类型,如果用一个变量去给const修饰的变量赋值,那么也会给const变量分配内存,编译器就不会进行优化了。

int b = 10;
const int a= b;
int* p = (int*)&a;
*p = 300;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;

 输出为:

a = 300
*p = 300

 (3)对于自定义数据类型,比如类对象,也会分配内存,编译器不能进行优化。

const Person person;  //未初始化age
//person.age = 50; //不可修改
//指针间接修改
Person* p = (Person*)&person;
p->age = 100;

尽量用const替代#define。

(1)#define没有数据类型,const修饰的变量有数据类型,可以进行数据类型检查。

#define MA 128
const short ma = 128;
void func(short a)
{
	cout << "func(short a)" << endl;
}
void func(int a)
{
	cout << "func(int a)" << endl;
}


int main()
{
	func(MA);  //传入MA时,只能调用到func(int a);
    func(ma);  //传ma时,就会进行类型检查了

	system("pause");
	return EXIT_SUCCESS;
}

 (2)const修饰的变量有作用域,#define不重视作用域,不能限定常量的使用范围。

7. 引用(重难点)

C++ 增加了另外一种给函数传递地址的途径,即按引用传递

  • 变量名实质上是一段连续内存空间的别名;
  • 程序中通过变量来申请并命名内存空间;
  • 通过变量名可以使用内存空间。

(1)引用可以作为一个已定义变量的别名

void test02()
{
	int a = 10;
	int& b = a; //给a的空间取别名叫b
	b = 100;
	cout << a << endl;
}

输出为:100

而指针的写法是:

void test02()
{
	int a = 10;
	int* b = &a; 
	*b = 100;
}

(2)当定义函数时,按引用传递

void func(int &b)
{
    b = 100;
}

void test()
{
    int a = 10;
    func(a);
    cout << "a = " << a << endl;
}

输出为:a = 100

而C指针的写法是:

void func(int *b)
{
    *b = 100;
}

void test()
{
    int a = 10;
    func(&a);
    cout << "a = " << a << endl;
}

引用的注意:

  • ① & 在此不是求地址运算,而是起标识作用;
  • ② 引用创建时必须初始化;(还有,创建常量时也需要初始化)
  • int& a; //err
    
  • ③ 引用一旦初始化后不能改变它的指向;
  • int a =10;
    int b = 20;
    
    int& c = a; //给a的空间取别名为c
    c = b; //err,不能改变引用
    
  • ④ 必须引用一块合法的内存空间;

(3)建立数组引用

int main()
{
	int arr[] = { 1,2,3 };
	//第一种方法
	typedef int(Arr)[3];  //定义数组类型
	Arr &arref = arr;     //建立引用

	//第二种方法,直接定义引用
	int(&arref2)[3] = arr;

	for (int i = 0; i < 3; i++)
	{
		cout << arref[i] << endl;
	}
}

(4)引用的本质

C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。即,引用的本质在C++内部实现是一个常指针。

int a = 10;
int& aRef = a; //自动转换为int* const aRef = &a;这也能说明引用为什么必须初始化
aRef = 20; //内部发现aRef 是引用,自动帮我们转换为: *aRef = 20;

 如下图所示:

(5)指针引用(重、难)

在C语言中,如果想改变一个指针的指向,而不是它所指向的内容,则会使用二级指针,如下:

void func(char** tmp)
{
	char* p = (char*)malloc(64);
	memset(p, 0, 64);
	strcpy(p, "hello");
	*tmp = p;
}

void test()
{
	char* mp = NULL;
	func(&mp);
	cout << mp << endl;
}

改成引用的话:

void func(char* &tmp)
{
	char* p = (char*)malloc(64);
	memset(p, 0, 64);
	strcpy(p, "hello");
	tmp = p;
}

void test()
{
	char* mp = NULL;
	func(mp);
	cout << mp << endl;
}

(6)常量引用

定义格式:

const int& ref = val;
int main()
{
	//普通引用
	int a = 10;
	int& tmp = a;
	tmp = 20;

	//int &ref = 10;  err,不能给字面量取别名
	const int& tmp1 = 10;  //可以给const修饰的引用赋予字面量
	//编译器会把上边的代码变为:int x=10; const int &tmp1=x;
    tmp1 = 20; //err,不能修改
}

(7)引用的使用

 常量引用主要用在函数的形参,尤其是类的拷贝构造函数。

将函数的形参定义为常量引用的好处:

  • 引用不产生新的变量,减少形参与实参传递时的开销;
  • 由于引用可能导致实参随形参而改变,将其定义为常量引用可以消除这种副作用;
  • 如果希望实参随着形参的改变而改变,那么使用一般的引用;如果不希望实参改变,那么使用常引用。

① 引用作为函数的参数

void func(int& a, int& b)
{
	int sum = a + b;
	cout << "sum = " << sum << endl;
}

void test()
{
	int a = 10;
	int b = 20;
	func(a, b);
}

通过引用参数产生的效果同按地址传递是一样的。引用的语法更简单:

  • 函数调用时传递的实参不必加“ & ”;
  • 在被调函数中不必在参数前加“ * ; 

② 引用作为函数的返回值

如果从函数中返回一个引用,必须像从函数中返回一个指针一样对待。当函数返回时,引用关联的内存一定要存在。

int& func()
{
	int b = 10;    //不能返回局部变量的引用
	int& p = b;
	return p;
}

int& func2()
{
	static int b = 10;
	return b;
}

void test()
{
	cout << "func=" << func() << endl;
	func() = 100;          //如果要函数当左值,那么该函数必须返回引用
	cout << "func2()=" << func2() << endl;
}

8. 内联函数

内联函数:

① 具有普通函数的所有行为。

② 会在适当地方向预定义宏一样展开,所以不需要函数调用时的开销。

③ 内联函数也占用空间,但是相对于普通函数,只是省去了函数调用时的压栈、跳转、返回的开销,可以理解为内联函数是“以空间换时间”

④ 在普通函数前加上 inline 关键字使之成为内联函数。但是必须注意,函数体和声明必须结合在一起,否则编译器将它作为普通函数来对待。

inline void func(int a); //该写法没有任何效果

类内部的内联函数:

类内部定义内联函数时并不必须要加 inline,任何在类内部定义的函数自动成为内联函数。

c++内联编译会有一些限制:

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

9. 函数的默认参数

函数的默认参数的作用:

当函数内常用到形参的某个值,但偶尔要使用其他值; 增加函数的灵活性。

int func(int a, int b = 0)  //b=0即为b的默认值
{
	return a + b;
}
void test()
{
    cout << func(1,2) << endl;
    cout << func(1) << endl;
}

注意:

  • 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数。
  • 函数声明和函数定义不能同时设置默认参数。

10. 函数的占位参数

void func(int a, int b, int)
{
    //函数内部无法使用占位参数
	cout << "a+b = " << a + b << endl;
}

//占位参数也可以设置默认值
void func2(int a, int b, int=20)
{
    //函数内部依旧无法使用占位参数
	cout << "a+b = " << a + b << endl;
}

int main()
{
	func(10,20);     //err,占位参数也是参数,必须传参数
	func(10,20,30);  //ok
	func2(10,20);    //ok
	func2(10,20,30); //ok

	return EXIT_SUCCESS;
}

后边讲的操作符重载的后置++要用到这个。

11. 函数传参的三种方式

值传递、指针传递、引用传递

//值传递
void swap(int a, int b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
//指针传递
void swap2(int *a, int *b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//引用传递
void swap3(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

void myprint(int& a, int& b)
{
	cout << "a=" << a << "b=" << b << endl;
}

int main()
{
	int a = 10;
	int b = 20;
	swap(a,b);
	myprint(a, b);
	swap2(&a,&b);
	myprint(a, b);
	swap3(a,b);
	myprint(a,b);
}

上述三种交换结果,只有第一种值传递无法交换a,b的值。

12. 创建一个学生类

class Student
{
public:    //公有
	void setName(string Name)  //成员方法,也叫成员函数
	{
		name = Name;
	}
	void setID(int Id)
	{
		id = Id;
	}
	void myprint()
	{
		cout << "姓名:" << name << "\n学号:" << id << endl;
	}
private:  //私有权限
	string name;  //成员属性
	int id;
};

int main()
{
	Student s;
	s.setName("Paul");
	s.setID(1);
	s.myprint();

	system("pause");
	return EXIT_SUCCESS;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值