c++核心编程
第一天
面向对象基本特征
封装,继承,多态。
命名空间
1.为什么有命名空间,是因为解决多人合作时取标识符重命名的问题。
2.什么是命名空间
//命名空间
namespace A{//A是空间的名字
int a;
void func()
{
}
}
3.命名空间的注意
注意1:命名空间只能写在全局
注意2:命名空间可以嵌套命名空间
namespace Maker
{
int a;
namespace B
{
int b;
}
}
注意3:命名空间是开放的,随时可以加入新成员,但是新成员只能在加入后使用。
注意4:匿名命名空间
注意5:命名空间可以取别名
注意6:分文件编写代码时,如果.h中有两个命名空间,但是里面的成员函数或成员变量同名时,在.cpp函数中需要添加命名空间。
作用域运算符
namespace Maker
{
int c=30;
}
//类似于static int d=50
namespace
{
d=50;
}
//命名空间取别名
void test01()
{
//
namespace nameMaker=Maker;
cout<<nameMaker::a<<endl;
}
int mya=10;
int main()
{
int mya=20;
//就近原则
cout<<"mya="<<mya<<endl;
cout<<"::mya="<<::mya<<endl;
cout<<Maker::c<<endl;
cout<<d<<endl;
system("pause");
return EXIT_SUCCESS;
}
1.作用:用来访问某个作用域里面的成员
using声明和编译命令
namespace A
{
int a=10;
int b=20;
int c=30;
}
void test01(){
//using 声明是让命名空间中某个标识符可以直接使用
using A::a;
cout<<a<<endl;
//int a=50;//注意:using声明了某个变量,在该作用域内不能定义同名的变量。
}
void test02(){
//using编译指令,让某个命名空间中的标识符都可以直接使用
using namespace A;
cout<<a<<b<<c<<endl;
int a=100;//可以。类似于命名空间中的a是全局变量,而这里是局部变量。
}
结构体的加强
//自定义数据类型
struct Maker
{
char name[64];
int age;
};
void test01()
{
Maker a;//不需要加struct就可以定义变量
}
//加强2
struct Maker2
{
int a;
void func()//结构体内可以写函数
{
cout<<"func"<<endl;
}
}
更加严格的类型转换检查
//这种方式不能进行隐式转换,必须显示的转换
char *p=(char *)malloc(64);
三目运算符
//c语言例子
void test01()
{
int a=10;
int b=20;
printf("%d\n",a>b?a:b);
//(a>b?a:b)=100;error
//这个表达式返回的是右值,是数值,返回的是20
//20=100;
*(a>b?&a:&b)=100;
printf("b=%d\n",b);
}
//c++例子
void test01()
{
int a=10;
int b=20;
//c++的三目运算符返回的是左值,是空间
(a>b?a:b)=100;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}
const修饰的变量
1.c语言的const修饰的变量都有空间
2.c语言的const修饰的全局变量具有外部链接属性
3.使用别的文件的全局const修饰的变量需要声明
c语言情况
const int a=10;//常量区,一旦初始化,不能修改;
void test01()
{
//a=200;全局的const不能直接修改
int *p=(int*)&a;
//*p=100;//全局的const不能间接修改
printf("%d\n",a);
}
int main()
{
const int b=20;//栈区
//b=200;局部的const修饰的变量不能直接修改
int *p=(int*)&b;
*p=200;//间接可以修改
printf("%d\n",b);
}
c++情况
1.c++语言的const修饰的变量有时有空间,有时没有空间(发生常量折叠,且没有对变量进行取址操作。)
2.c++语言中const修饰的全局变量具有内部链接属性。
3.在const前加上extern就变为外部连接属性。
4.自定义数据类型,编译器不能优化。
5.如果用变量给const修饰的局部变量赋值,那么编译器不会优化。
const int aa=10;//没有内存
void test01()
{
//发生了常量折叠
cout<<"aa="<<aa<<endl;//在编译阶段,编译器:cout<<"aa="<<10<<endl;
//禁止优化volatile
volatile const int bb=20;
//const int bb=20;//栈区
int *p=(int*)&bb;
*p=200;
cout<<"a的地址:"<<(int)&bb<<endl;
cout<<"p指向的地址"<<(int)p<<endl;
cout<<"bb="<<bb<<endl;//结果为20 编译器优化的结果为cout<<"bb="<<20<<endl
}
int main()
{
test01();
}
引用的描述
1.引用是做什么:和c语言的指针一样的功能,而且使语法更加简洁。
2.引用是什么:给空间取别名。
3.引用创建时,必须要先初始化。
4.引用一旦初始化不能改变它的指向。
5.引用必须引用一块合法的内存空间。
int a=10;
int &b=a;//给a的空间取别名叫b
b=100;
cout<<a<<endl;
void func(int &a)
{
a=200;
}
void test2()
{
int a=10;
func(a);
cout<<"a="<<a<<endl;
}
//引用一旦初始化不能改变它的指向
int a=10;
int &pRef=a;//给a的空间取别名叫pRef;
int b=20;
pRef=b;//赋值操作
cout<<&a<<endl;
cout<<&pRef<<endl;//两个地址一样
数组的引用
int arr[]={1,2,3,4,5};
//第一种方法
//1.定义数组类型
typedef int(MY_ARR)[5];//数组类型
//建立引用
MY_ARR &arrf=arr;//建立引用,int &b=a;
//第二种方法
//直接定义引用
int (&arref2)[5]=arr;//int &b=a;
//第三种方法
typedef int(&MY_ARR3)[5];//建立引用数组类型
MY_ARR3 arref3=arr;
尽量用const替代define
1.define无数据类型,const有数据类型。
2.const修饰的变量有作用域,define修饰的变量无作用域。
指针的引用
char* p="李明";
char* &p1=p;
cout<<p1<<endl;
//二级指针,用来装一级指针变量的地址
//被调函数
void func(char**temp)
{
char *p=(char*)malloc(64);
memset(p,0,64);
strcpy(p,"小花");
*temp=p;
}
char* mp=NULL;
fun(&mp);
cout<<mp;
引用的使用场景
1.引用作为函数参数
2.引用作为函数的返回值
常量引用和bool类型
//普通引用
int a=10;
int &ref=a;
ref=20;
//int &ref2=10;//不能给字面变量取别名
const int &ref3=10;//可以给const修饰的引用赋予字面量
//const修饰符修饰的引用的原理
//编译器会把上面的代码变为:int tmp=10;const int &ref3=tmp;
内联函数
//宏函数
#define ADD(x,y) x+y
//在普通函数前面加上inline是向编译器申请成为内联函数
//注意:加inline可能成为内联函数,也可能不能成为内联函数
//什么情况不会成为内联函数
//1.存在过多的条件判断语句
//2.函数体过大
//3.对函数进行取址操作
//内联函数的优势:有宏函数的效率,没有宏函数的缺点。
//类的成员函数默认加上inline
inline int Add(int x,int y)
{
return x+y;
}
void test()
{
//10+20*2
int ref=ADD(10,20)*2;
cout<<"red="<<ref<<endl;
}
#define COMPAD(x,y) ((x)<(y)?(x):(y))
void test2()
{
int a=1;
int b=3;
// ((++a)<(b)?(++a):(b)) a自增两次
cout<<"COMAPD(x,y)="<<COMPAD(++a,b)<<endl;//结果为3
}
函数的默认参数和占位参数
1.注意1:函数的默认参数后面的参数必须都是默认参数
2.注意2:函数的声明和实现不能同时有函数的默认参数
int myFunc(int a,int b=0)//int b=0;这就是函数的默认参数,不一定是0
{
return a+b;
}
void test01()
{
//函数的默认参数的作用
//当函数内常要用到形参的某个值,但偶尔要使用其他值
cout<<myFunc(10,20)<<endl;
cout<<myFunc(10)<<endl;
}
void myFun3(int a,int b);
void myFun3(int a,int b=0)
{
}
//函数的占位参数,占位参数在后面区分前加加或后加加
void func(int a,int b=10)//占位参数也可以有默认值
{
}
func(10);
函数重载
1.在c++中允许出现同名的函数,这种现象称为函数重载。
2.函数重载的作用:是为了方便使用函数名。
3.函数重载的条件:参数的个数不同,同一个作用域,参数的顺序不同,参数的类型不同。
满足四个中任意一个即可。
4.函数的返回值不能作为函数重载的条件
编译器是通过你调用函数时,传入的参数来判断调用重载的哪个函数,我们调用函数时不需要写返回值,所以返回值不能作为函数重载的条件。
函数重载的原理
1.函数重载的原理是在汇编时,给各个函数取别名,c语言不能重载的原因是没有取别名。
c++调用c语言的函数
1.c++的函数在汇编时,会给函数取别名,c语言不会,这时,如果c++来调用c语言的函数,c++会取找取了别名的函数,但是c语言没有取别名,这时会出错。
//这是在告诉c++编译器,找下面的函数,要以c语言的方式去寻找。
#ifdef __cplusplus
extern "C"
{
#endif
void func();
#ifdef __cplusplus
}
#endif
构造函数和析构函数的概念
//构造函数的作用是初始化成员变量,是编译器去调用的
//实例化对象,内部做了两件事,1.分配空间2.调用构造函数进行初始化
匿名对象
1.匿名对象的生命周期在当前行
Maker();
//注意,如果匿名对象有名字来接,那么就不是匿名对象。
Maker m1=Maker();
构造函数的调用规则
//如果程序员提供了有参构造,那么编译器不会提供默认的构造函数,但是会提供默认的拷贝构造。
//如果程序员提供了拷贝构造函数,那么编译器不会提供默认的构造函数和默认的拷贝构造函数。
多个对象的构造函数和析构函数
//如果类有成员对象,那么先调用成员对象的构造函数,在调用本身的构造函数。
//析构函数的顺序反之
//2.成员对象的构造函数调用和定义顺序一样
//3.注意,如果有成员对象,那么实例化对象时,必须保证成员对象的构造和析构能被调用。
//4.如果有多个对象需要指定调用某个构造函数,用逗号隔开。
//5.可以使用对象的构造函数传递数值给成员对象的变量。