C++基础

目录

1. C++对C语言的加强

1.1 namespace命名空间

1.2 “实⽤用性”增强

1.4 三⽬目运算符功能增强

1.5 const 增强

1.6 枚举的增强

2. 引用

2.1 规则

2.2 引用作为函数参数

2.3 引用的意义

2.4 引用的本质

2.5 引用作为函数的返回值

2.6指针的引用

2.7const引用

3 inline内联函数

4 函数重载

4.1重载规则

4.2函数重载与函数指针

4.3类的封装

5 面向过程与面向对象

6 对象的构造与析构

6.1构造函数

6.2 析构函数

6.3 拷贝构造函数

6.4 浅拷贝与深拷贝

6.5 构造函数初始化列表

7 对象动态建⽴和释放 new 和delete

8 静态成员变量与成员函数

9 this指针(是一个常指针)

10 全局函数与成员函数

11 友元函数

12 运算符重载

13 继承

13.1如何恰当的使用public,protected和private为成员声明访问级别?

13.2继承中的构造和析构

13.3继承中构造析构调用原则

13.4派生类中的static关键字

13.5多继承与虚继承

14 多态(多态是设计模式的基础,多态是框架的基础)

14.1那么如何实现多态呢?

14.2多态成立的条件:

14.3虚析构函数

14.4重载、重写、重定义

14.5纯虚函数和抽象类


 


1. C++对C语言的加强

1.1 namespace命名空间

在C++中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(命名空间),可以更好地控制标识符的作用域。所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。std是c++标准命名空间,c++标准程序库中的所有标识符都被定义在std中,比如标准库中的类iostream、vector等都定义在该命名空间中,使用时要加上using声明(using namespace std) 或using指示(如std::string、std::vector<int>).

由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择: 

1)直接指定标识符。例如std::ostream。完整语句如下:

std::cout  <<  std::hex  <<  3.4  <<  std::endl; 

2)使用using关键字。

using std::cout; 
using std::endl; 

以上程序可以写成 :

cout  <<  std::hex  <<  3.4  <<  endl; 

3)最方便的就是使用using namespace std; 例如:using namespace std;这样命名空间std内定义的所有标识符都有效。就好像它们被声明为全局变量一样。

C中的命名空间:
在C语言中只有一个全局作用域;
C语言中所有的全局标识符共享同一个作用域;
标识符之间可能发生冲突。

C++中的命名空间:
命名空间将全局作用域分成不同的部分;
不同命名空间中的标识符可以同名而不会发生冲突;
命名空间可以相互嵌套;
全局作用域也叫默认命名空间。

编程训练:

#include <stdio.h>
namespace NameSpaceA
{
    int a = 0;
}

namespace NameSpaceB
{
    int a = 1;
    namespace NameSpaceC
    {
        struct teacher
        {
            char name[10];
            int age;
        }; 
    }
}

int main(void)
{
    using namespace NameSpaceA;//方法3
    using NameSpaceB::NameSpaceC::teacher;//方法2或者方法3using namespace NameSpaceB::NameSpaceC;  
	//using namespace NameSpaceB::NameSpaceC; 
    teacher t1 = {"Tina",  33};
    printf("a  =  %d\n",  a);   //0 
    printf("a  =  %d\n",  NameSpaceB::a);//1,方法1
    
    printf("t1.name  =  %s\n",  t1.name);   //Tina
    printf("t1.age  =  %d\n",  t1.age);     //33 
    return  0; 
}

1.2 “实⽤用性”增强

C语⾔言中的变量都必须在作⽤用域开始的位置定义;
C++中更强调语⾔言的“实⽤用性”,所有的变量都可以在需要使⽤用时再定义。

#include  <iostream> 
using  namespace  std;

int  main(void) 
{ 
    int  i  =  0; 
    cout  <<  "i  =  "  <<i  <<endl; 
    int  k; 
    k  =  4; 
    cout  <<  "k  =  "  <<k  <<endl; 
    return  0; 
} 

1.3 struct 类型增强

C语⾔言的struct定义了⼀一组变量的集合,C编译器并不认为这是⼀一种新的类型 ;
C++中的struct是⼀一个新类型的定义声明 。

#include  <iostream> 
struct  Student 
{ 
    char  name[100]; 
    int  age; 
}; 
int  main(int  argc,  char  *argv[]) 
{ 
    Student  s1  =  {"Tina",  12}; //可以不加关键字struct,但在C语言中一定要加
    Student  s2  =  {"Tom",  21}; 
    return  0; 
} 

1.4 三⽬目运算符功能增强

1)C语言返回变量的值, C++语言是返回变量本身
C语言中的三目运算符返回的是变量值,不能作为左值使用
C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方。
2)注意:三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用:(a < b ? 1 : b )= 30;

#include  <iostream> 
using  namespace  std; 
int  main(void) 
{ 
    int  a  =  10; 
    int  b  =  20; 
    //返回⼀一个最⼩小数  并且给最⼩小数赋值成30 
    //三⺫⽬目运算符是⼀一个表达式  ,表达式不可能做左值 
    (a  <  b  ?  a  :  b  )  =  30; 
    printf("a  =  %d,  b  =  %d\n",  a,  b); //输出结果为30,20
    return  0; 
} 

1.5 const 增强


//第一个第二个意思一样  代表一个常整形数 
const  int  a; 
int  const  b;

//第三个  c是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本⾝身可以修改),常在函数声明时修饰参数的作用
const  int  *c; 

//第四个  d  常指针(指针变量不能被修改,但是它所指向内存空间可以被修改) 
int  *  const  d; 

//第五个  e一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改) 
const  int  *  const  e  ; 
    

const在函数声明时修饰参数:

void *memmove( void* dest, const void* src, size_t count ); 这是标准库中的一个函数,在头文件#include <string.h>中声明,其功能为由src所指内存区域复制count个字节到dest所指内存区域。用于按字节方式复制字符串(内存)。它的第一个参数,是将字符串复制到哪里去(dst),是目的地,这段内存区域必须是可写。它的第二个参数,是要将什么样的字符串复制出去,我们对这段内存区域只做读取,不写。于是,我们站在这个函数自己的角度来看,src 这个指针,它所指向的内存内所存储的数据在整个函数执行的过程中是不变。于是src所指向的内容是常量。于是就需要用const修饰。

C语言中const变量是只读变量,有自己的存储空间(通过指针可以被修改)

#include <stdio.h>
int  main(void) 
{ 
	const int a=10;
	printf("%d\n",a);
	int *p = (int*)&a;
	*p=11;
	printf("%d\n",a);//输出11,a的值被改变。C语言中const变量是只读变量,有自己的存储空间
	return 0;
	
} 

C++中的const常量(不能被修改)

#include <iostream> 
using namespace std; 
int main(void) 
{ 
	const int a=10;
	printf("%d\n",a);
	int *p = (int*)&a;
	*p=11;
	printf("%d\n",a);//输出为10
	printf("%d\n",*p);//输出为11
	return 0;
}

上述代码中的const int a=10并没有像C语言中那样为a在栈中开辟存储区域,而是在常量区的符号表中增加了一个key与value,key即a,value即10。在这里,a并没有所谓的空间与地址,类似于宏定义。但宏定义是在预处理阶段展开,而后者是在编译阶段。当使用&操作符,取const常量的地址时,会分配临时存储空间,上述代码中指针p即存放该临时空间的地址,修改也只是修改该临时空间的值,a值并不改变。

C++中的const修饰的,是一个真正的常量,而不是C中变量(只读)。在const修饰的常量编译期间,就已经确定下来了。故下面代码是合法的,而在C语言中就不合法。在C中a被修饰为只读变量,可惜再怎么修饰也不是常量。而ANSI C规定数组定义时长度必须是“常量”,在栈中分配相应固定大小的存储空间。

const int a =10;
int array[a] = {0};

1.6 枚举的增强

c 语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而 c++中枚举变量, 只能用被枚举出来的元素初始化。

2. 引用

变量名,本身是一段内存的引用。 引用可以看作一个已定义变量的别名。 引用的语法:Type& name = var;

2.1 规则

1 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故 而类型与原类型保持一致,且不分配内存。与被引用的变量有相同的地址。
2 声明的时候必须初始化,一经声明,不可变更。
3 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
 4 &符号前有数据类型时,是引用。其它皆为取地址。

#include<iostream>

using namespace std;

int  main(void) 
{ 
    int  a,b; 
    int  &r  =  a; 
    int  &r  =  b;  //错误,不可更改原有的引⽤用关系 
    float  &rr  =  b;  //错误,引⽤用类型不匹配  
    cout<<&a<<&r<<endl;  //变量与引⽤用具有相>同的地址。 
    int  &ra  =  r;  //可对引⽤用更次引⽤用,表⽰示  a  变量有两个别名,分别是  r  和  ra 
    return  0; 
} 

2.2 引用作为函数参数

普通引用在声明时必须用其它的变量进行初始化,引用作为函数参数声明时不进行初始化。

#include  <iostream> 
using  namespace  std; 
struct  Teacher 
{ 
    char  name[64]; 
    int  age  ; 
}; 

void  printfT(Teacher  *pT) 
{ 
    pT-­>age = 32;
    cout<<  pT-­>age  <<endl; 
} 

void  printfT2(Teacher  &pT) //当传入参数t1,pT是t1的别名,相当于修改了t1 
{ 
    pT.age  =  33; 
    cout<<pT.age<<endl; 
} 
//pT和t1的是两个不同的变量 
void  printfT3(Teacher  pT)//值拷贝,当参数传入后相当于拷贝整个结构体
{ 
    cout<<pT.age<<endl; 
    pT.age  =  45;  //只会修改pT变量  ,不会修改t1变量 
} 
int  main(void) 
{ 
    Teacher  t1; 
    t1.age  =  35; 
    printfT(&t1); //32
    printfT2(t1);  //pT是t1的别名 输出33
    printf("t1.age:%d  \n",  t1.age);  //33 
    printfT3(t1)  ;//  pT是形参  ,t1  copy⼀一份数据  给pT 
    printf("t1.age:%d  \n",  t1.age);  //33 
    return  0; 
} 

2.3 引用的意义

引用作为其它变量的别名而存在,因此在一些场合可以代替指针。c++中引入引用后,可以用引用解决的问题。避免用指针来解决。

void  swap(int  a,  int  b);  //⽆无法实现两数据的交换 
void  swap(int  *p,  int  *q);  //开辟了两个指针空间实现交换 

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

2.4 引用的本质

#include <iostream> 
using namespace std; 
 struct Taacher1
 {
 	int &age;
 };
  struct Taacher2
 {
 	int *age;
 };
int main(void) 
{ 
	cout<< sizeof(struct Taacher1)<<" ,"<<sizeof(struct Taacher1)<<endl;//输出8,8
	return 0;
}

1)引用在C++中的内部实现是一个常指针 ,初始化只够都不能再被修改
 Type& name <===> Type* const name
2)C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
3)从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏。

2.5 引用作为函数的返回值

当函数返回值为引用时:

①若返回栈变量:不能成为其它引用的初始值(也不能作为左值使用);

②若返回静态变量或全局变量:可以成为其他引用的初始值(可作为右值使用,也可作为左值使用);

 

include <iostream> 
using namespace std; 
int getA1() 
{ 
    int a; 
    a  = 10; 
    return  a; 
} 
int& getA2() 
{ 
    int a; 
    a = 10; 
    return a; 
} 

int& getA3() 
{ 
    static int a; 
    a = 10; 
    return a; 
} 
int  main(void) 
{ 
    int a1 = 0; 
    int a2 = 0; 
    //值拷⻉贝 
    a1 = getA1(); 
    //将⼀一个引⽤用赋给⼀一个变量,会有拷⻉动作 
    //理解:  编译器类似做了如下隐藏操作,a2  =  *(getA2()) 
    a2 = getA2(); 
    //将⼀一个引⽤用赋给另⼀一个引⽤用作为初始值,由于是栈的引⽤用,内存非法 
    int &a3 = getA2(); 
    //将⼀一个引⽤用赋给另⼀一个引⽤用作为初始值,由于是静态区域,内存合法 
    int  &a4 = getA3(); 

    cout <<"a1 = " <<a1<<endl; 
    cout <<"a2 = " <<a2<<endl; 
    cout <<"a3 = " <<a3<<endl; 
    return 0; 
}

③如果返回值为引用可以当左值,如果返回值为普通变量不可以当左值。

#include <iostream> 
using  namespace std; 
//函数当左值 
//返回变量的值 
int func1() 
{ 
    static int a1 = 10; 
    return a1; 
} 
//返回变量本⾝身  ,  
int& func2() 
{ 
    static int a2 = 10; 
    return a2; 
} 
int main(void) 
{ 
    //函数当右值 
    int c1 = func1(); 
    cout << "c1 = " << c1 <<endl; 
    int  c2 = func2(); //函数返回值是⼀一个引⽤用,并且当右值  
    cout << "c2 = " << c2 <<endl; //输出为10
    //函数当左值 
    //func1() = 100; //error 
    func2() = 100;   //函数返回值是⼀一个引⽤用,并且当左值  
    c2 = func2(); 
    cout << "c2 = " << c2 <<endl; //输出为100
    return 0; 
} 

2.6指针的引用

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

struct teacher
{
	int id;
	char name[64];
};

int getMem(struct teacher ** tpp)
{
	struct teacher *tp = NULL;
	tp = (struct teacher*)malloc(sizeof(struct teacher));
	if (tp == NULL)
	{
		return -1;
	}
	tp->id = 100;
	strcpy(tp->name, "lily");

	*tpp = tp;
	return 0;
}

void freeTeacher(struct teacher ** tpp)
{
	if (tpp == NULL)
	{
		return;
	}
	struct teacher *tp = *tpp;
	if (tp != NULL)
	{
		free(tp);
		tp = NULL;
	}
}
//不用指针,用引用
int getMem2(struct teacher * &tp)
{
	tp = (struct teacher *)malloc(sizeof(struct teacher));
	if (tp == NULL)
	{
		return - 1;
	}
	tp->id = 100;
	strcpy(tp->name, "tony");
	return 0;
}

void freeTeacher2(struct teacher * &tp)
{
	if (tp != NULL)
	{
		free(tp);
		tp = NULL;
	}
}
int main(void)
{
	struct teacher *tp = NULL;

	getMem(&tp);
	cout << "id=" << tp->id << ",name=" << tp->name << endl;
	freeTeacher(&tp);

	getMem2(tp);
	cout << "id=" << tp->id << ",name=" << tp->name << endl;
	freeTeacher2(tp);

	system("pause");
	return 0;
}

2.7const引用

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>

using namespace std;

//const修饰引用,一般跟const修饰指针的用途一样,都是作为函数参数,保证该参数是输入参数,是只读,
//在函数内部改变不了外部的值
void printB(const int &re)
{
	cout << "re = " << re << endl;
}

int main(void)

{
	const int a = 10;
	//int &re = a;//err
	//如果想对一个常量进行引用,必须是一个const引用
	const int &re = a;//ok

	int b = 20;
	printB(b);

	const int &re2 = 10;
	//编译器临时开辟一个变量temp=10;const int &re3 = temp

	system("pause");
	return 0;
}

结论:
1)const int & e 相当于 const int * const e
2)普通引用 相当于 int *const e
3)当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值
分配空间,并将引用名作为这段空间的别名
4)使用字面量对const引用初始化后,将生成一个只读变量

3 inline内联函数

c 语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销(编译器的压栈入栈)。但是由于宏函数的处理发生在预处理阶段,缺失了语法检测和有可能带来的语意差错。 C++提供了inline 关键字,实现了真正的内嵌。适用场景:函数体很“小”,且被“频繁”调用。

#include  <iostream> 
using  namespace  std; 
inline  void  func(int  a) 
{ 
    a  =  20; 
    cout  <<  a  <<endl; 
} 
int  main(void) 
{ 
    func(10); 
    /* 
       //编译器将内联函数的函数体直接展开 
    { 
        a  =  20; 
        cout  <<  a  <<endl; 
    } 
     
    */ 
    
    return  0; 
} 

特点:
1)内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。
2)C++编译器直接将函数体插入在函数调用的地方 。
3)内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)。
4)内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型等)。
5)内联函数由 编译器处理,直接将编译后的函数体插入调用的地方, 宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程。

6)C++中内联编译的限制:

不能存在任何形式的循环语句 ,不能存在过多的条件判断语句 ,函数体不能过于庞大 ,不能对函数进行取址操作 ,函数内联声明必须在调用语句之前。

7)编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。

4 函数重载

函数重载(Function Overload):用同一个函数名定义不同的函数(函数参数不同),当函数名和不同的参数搭配时函数的含义不同。

4.1重载规则

1,函数名相同。
2,参数个数不同,参数的类型不同,参数顺序不同,均可构成重载。
3,返回值类型不同则不可以构成重载。

4.2函数重载与函数指针

当使⽤用重载函数名对函数指针进⾏行赋值时,根据重载规则挑选与函数指针参数列表⼀一致的候选者 ,严格匹配候选者的函数类型与函数指针的函数类型。

#include  <iostream> 
using  namespace  std; 
int  func(int  x)  //  int(int  a) 
{ 
    return  x; 
} 
int  func(int  a,  int  b) 
{ 
    return  a  +  b; 
} 
int  func(const  char*  s) 
{ 
    return  strlen(s); 
} 
typedef  int(*PFUNC)(int  a);  //  int(*)(int  
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值