1.面向过程与面向对象的编程
什么是面向过程编辑呢?
举一个例子,我们去实现玩一个下棋游戏的项目,那么我们需要对下棋的所有功能进行实现,从游戏角色,进入游戏,游戏游玩,游戏输赢的判断,退出游戏等所有的过程我们都需要一步步实现。我们需要去分析每一步是如何实现的,这个过程就是面向过程的编译。
那什么是面向对象编程?
面向对象是相对于面向过程一步步实现的特点,面向对象更倾向于模块化的实现,对于’’对象‘‘,是系统中用来描述客观事物的一个实体,它是用来构成系统的一个基本单位,对象是由一组属性与一组行为构成的。
对象: 世界上任何的事物都可以被抽象成一个对象(属性 +行为)。对于一个下棋游戏,他的属性就是有棋盘,棋子加判断游戏输赢的功能的这样的行为。
2.面向对象编程的三大特点
封装 将属性和方法封装在一起抽象成一个类 并且对类中的成员加以权限控制
继承 将一个类中属性和方法继承到另一个类中
多态 一个接口 对种形态 (静态多态 动态多态)
面向对象编程相对于面向过程编程,更加模块化,是结构化程序因此能有效的将一个都咋的程序设计任务分解成许多易于控制的和处理的子任务,便于开发与维护。
3.c++对c的扩展:
在此之前我们先提一个在C++比较重要的运算符
1.作用域运算符::
:: 运算符前代表的是一个作用域 。
它的作用是解决归属问题(谁是属于谁的谁),A::变量B,B是来自A的一个变量。
如果::前面什么都没有加 代表是全局作用域,则可以将该变量转化为全局变量。
比如:
#include <iostream> using namespace std; int a = 100; void test01() { int a = 10; cout << a << endl;//输出局部变量a cout << ::a << endl;//输出全局变量a } int main() { test01(); return 0; }
可以看到这里的::a是一个全局变量了。
2.命名空间
创建名字是程序设计中一项基本的活动,当一个项目很大时,他会不可避免地包含大量名字,c++允许我们对名字的产生和名字的可见性进行控制。我们之前学习c语言可以通过static静态修饰全局变量使丢掉了外部连接属性,只对内部产生作用,在c++中我们可以定义一个作用域来控制对名字的访问。
1.c++命名空间(namespace)
在c++中,名称可以是符号常量,变量,函数,结构,枚举,类和对象等等。我们所创建的工程越大,名字的访问就越有可能发生冲突,其次在使用多个厂商的类库时,也可能会名字冲突。为了避免这样的冲突,引入关键字namespace给出作用空间,能更好的使用名称。
利用namespace我么们可以定义一片区间,其本质是作用域,为的是可以更好的控制标识符的作用域,其次编译器能通过空间名能快速地找到该数据。
命名空间之后 就可以存放 变量 函数 类 结构体 ...各种数据。
2.命名空间的使用
namespace 空间名称
{
存放在该空间的各种数据
}
其次命名空间是有许多特点的:
1.在不同命名空间内可以创建相同的名称
举一个实例,创建两个命名空间 A B分别在里面创建一个名字相同变量,计算机仍可以识别。
#include<iostream> using namespace std; 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 } int main() { test(); return 0; }
2.命名空间只能在全局范围内定义
错误写法
这里会报错,不允许在这里命名,必须在全局范围内,在函数内部也是错误写法。
3.命名空间可以嵌套
namespace A { int a = 20; namespace B { int a = 10; } } void test() { cout << "A::a :" << A::a << endl;//20 cout << "B::a :" << A::B::a << endl;//10 } int main() { test(); return 0; }
可以嵌套命名空间,但在访问名字时注意作用域。
4.命名空间是开放的,可以随时定义新成员到空间中。
namespace A { int a = 20; } namespace A { int b = 10; } void test() { cout << "A::a :" << A::a << endl;//20 cout << "A::a :" << A::b << endl;//10 } int main() { test(); return 0; }
在定义新成员时,编译器会自动将之前的成员与现在定义的合并在一起。
5.声明和实现可分离
比如声明一个函数,我么既可以在内部直接实现,也可以在外部通过作用域符号实现。
namespace A { int b = 10; void test2(); /* void test2() { cout << "A::b :" << A::b << endl; } */ } void A::test2() { cout << "A::b :" << A::b << endl; } int main() { A::test2();//10 return 0; }
这里注意必须要使用作用域符号,否则该函数是被认为未在该空间的。
6.无名的命名空间
定义无名的命名空间这里编译器默认为只在该源文件内部可以使用,相当于c中static修饰只能在内部链接,失去了外部连接属性。
但再在定义变量时注意不能与无命名空间里的重命名,否则无法判断,认为是重定义了。
7.命名空间别名
namespace verylongname { int a = 10; void fun() { cout << "haha" << endl; } } namespace A = verylongname; int main() { A::fun(); cout << "A::a :" << A::a << endl; return 0; }
3.using声明 命名中的空间成员 可用
using编译指令使整个命名空间标识符可用.
并且命名空间标识符如果和局部变量的标识符同名,不会有冲突,优先使用局部变量。
但同时存在弊端。
我们先看直接声明命名空间A后,直接使用A中的成员。
#include <iostream> using namespace std; namespace A { int a = 10; void out() { cout << "haha" << endl; } } int main() { using namespace A; cout << "A::a为" <<a<< endl;//10 out();//haha return 0; }
我么也可以声明各个成员再使用:
namespace A { int a = 10; void out() { cout << "haha" << endl; } } int main() { using A::a; using A::out; cout << "A::a为" <<a<< endl;//10 out();//haha return 0; }
注意: 当using声明的标识符和其他同名标识符有作用域的冲突时,会产生二义性
比如:
#include <iostream> using namespace std; namespace nameA { int a = 10; void foo() { cout << "hello using" << endl; } } void test01() { //注意: 当using声明的标识符和其他同名标识符有作用域的冲突时,会产生二义性 int a = 100; using nameA::a; using nameA::foo; cout << a << endl; cout << a << endl; cout << a << endl; foo(); } int main() { test01(); return 0; }
编译器不知道该变量a到底是属于哪一个a,编译器会报错using声明导致多次声明该变量。
因此最安全的方法是通过作用符号来访问命名空间成员。
using声明成员碰到函数重载
namespace A { void func() { } void func(int x) { } int func(int x, int y) { } } void test() { using A::func; //因为它们重名,这里访问了空间里的所有函数 //编译器根据参数或类型,返回来行等看是哪一个函数 }
这里不会产生二义性,但函数一定是有区别的。
这里需要总要说明两点:
4.C++中形参必须有类型,返回值和实参个数做检测
c语言中的函数的形参类型可以不写,没有返回值可以返回,实参的个数不
做检测,
void foo(x,y) { return 100; } void test01() { foo(1); foo(1, 2); foo(1,2,3); }
但在c++不行,c++语言中的函数的形参类型必须写,没有返回值不可以返回,实参的个
数做检测
void foo(x, y) // 编译器报错 形参没有类型 { return 100; //编译器报错 没有返回值但是返回了 } void test01() { foo(1);//实参的个数和形参的个数不一致 foo(1, 2); foo(1, 2, 3);//实参的个数和形参的个数不一致 }
我们在c++中函数名可以重复,编译器会根据函数的返回类型,参数的类型,参数的个数来确定你是其中那一个函数,因此必须要写。
5.更严格的类型转换
c++中对类型转换有严格的要求,需要的类型和给的类型不一致时,可能会编译保存
例如.c语言中这段代码可以编译通过:
void test02() { char * p = malloc(100); }
但是在c++中这段代码编译不通过,需要做类型转换
void test02() { char * p = (char*)malloc(100); }
6.结构体增强
c中定义结构体变量时需要struct定义,在c++中不需要。
如简单定义一个学生A
struct student { int age; string name; char sex; }; int main() { student A={10,"zhansan",'nan'}; cout << "A学生的年龄为:" << A.age << endl;//10 return 0; }
其次还有不同
在结构体中定义函数
struct student { int age; string name; char sex; void setname(string newname) { name = newname; } void steage(int newage) { age = newage; } }; int main() { student A={10,"zhansan",'nan'}; cout << "A学生的年龄为:" << A.age << endl;//10 A.setname("lisi"); cout << "A学生的姓名为:" << A.name << endl; return 0; }
这里我们可以学习到关于string函数的一个认识,
string str:生成空字符串 string s(str):生成字符串为str的复制品 string s(str, strbegin,strlen):将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值 string s(cstr, char_len):以C_string类型cstr的前char_len个字符串作为字符串s的初值 string s(num ,c):生成num个c字符的字符串 string s(str, stridx):将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值 eg: string str1; //生成空字符串 string str2("123456789"); //生成"1234456789"的复制品 string str3("12345", 0, 3);//结果为"123" string str4("012345", 5); //结果为"01234" string str5(5, '1'); //结果为"11111" string str6(str2, 2); //结果为"3456789"
7:新增bool类型关键字
c++中可以直接使用bool类型
在c语言中,一下代码中的bool类型,需要包含stdbool.h头文件,但是在c++可以直接使用
void test04() { // bool类型的变量只有两个值 true false //true 和false 可以直接当成常量来用 bool flag = true; )
8.三目运算符功能增强
c++中的三目运算符表达式返回的可以是一个变量,但是c语言中返回的是一个常量
c语言中:
//三目运算符 void test05() { int a = 10; int b = 20; printf("%d\n", a < b ? a : b); //在c语言中三目运算符返回的是表达式的值,是一个常量 //(a < b ? a : b) = 100; 编译报错 *(a < b ?&a :&b) = 100; }
c++中:
//三目运算符 void test05() { int a = 10; int b = 20; printf("%d\n", a < b ? a : b); //在c++语言中三目运算符返回的是变量 (a < b ? a : b) = 100;//编译可通过 }
c++中返回变量,可以被修改,c语言返回常量无法被修改。