1. 类类型和对象的概念
(1)概念简单解释
(1)对象:指的是具体的东西
(2)类/类型:抽象的模型
(2)站在程序数据类型的角度看类和对象
(1)站在基本类型的角度
(1)int, double, char, short等基本类型都是抽象的模型
(2)使用基本类定义的变量或者常量就是对象
int a=10;
const int b = 20;
a和b都是对象
(2)站在自定义类型(复杂类型来说)
(1)数组,结构体,联合体,类类型都是抽象模型。
struct A
{
};
uinon B
{
};
class C
{
};
(2)定义的变量(实例)就是对象。
struct A a;//c++中可以为 A a
uinon B b; //c++中可以为 B b
C c;
以上的a,b,c都是具体的对象。
从以上的举例可以看出,对于语言来说,类与对象的概念是一早就有的,因为
语言适用于描述现实世界的,世界就是类和对象的关系,反映在语言中,显然
也逃不出类和对象的影子。
但是在面向过程的语言中,哪些基本类型和一些简单的自定义类型(结构体,
联合)的类和对象的关系还非常的低级,不足以去描述的真实世界各种复杂的
类与对象的关系,因此面向对象语言诞生。
(3)面向对象编程只是一种编程思维
面向对象编程只是一种编程思维,其实任何一种语言都可以使用面向对象的思维
去编程,但是最后计算机去执行的时候通通的都是面向过程的方式去运行的。
由于面向对象的语言的语法特性优化,非常合适人类使用面向对象的思想开发程序,
但是代价是过程中编译程序的复杂程度就会增加,而且会增加更多的管理手段,
相比底层语言来说,器运行小的效率低很多,但是好处是开发效率提高很多。
就现目前来说,如果不考虑哪些受到硬件资源限制的特殊亲嵌入式设备的话,实际
上开发者更多应该慨率的是如何提高程序的开发效率,这个时候就应该选择更加上
层的面向对象的语言。
2. 自定义类型之结构体
(1)c++中结构体的特点
c++中的类在功能上已经可以完全的替换结构体,但是c++为什么还保留了c中的结构体
呢,除了c++中有的时候也需要使用结构体外,更主要的原因是因为是为了保持和
c的兼容性。
c++中的结构体功能完全兼容c中结构体所有功能,但是同时也有一些改动,因此
学习c++的结构体应该先站在c结构体的角度学习,然后再站在c++的角度去看看
它们有哪些异同,这样才能够充分的把握结构体的使用。
结构体在c++中虽然有弱化趋势,但是结构体是c语言中非常重要的自定义数据类
型,所以学好结构体是充分把握c和c++这两门语言的很好的契机。
(3)出现结构体的原因
由于数组这种自定义类型要求每个元素必须是相同的类型,在面对很多需要描述
复杂一点事物的时候显得很不给力,因为描述复杂事务往往需要很多不同的数据
类型去描述它的各种属性,比如有些属性需要字符串,有些属性需要整形变量,
而有些属性又需要浮点。
(4)c语言中的结构体
(1)定义结构体类型和定义结构体边来给你的集中情况
(1)情况1
struct A
{
};
struct A a;
(2)情况2
struct A
{
}a;
(3)情况3
struct
{
}a;
使用这种情况时,由于其结构体类型的名称被省略了,因此定义结构图
体变量时必须采用这样的方式定义,因为该方式有这种限制,实际上这
样的方式用的并不多。
其中,结构体名字与结构体变量名,结构体成员名可以重名,不会冲突。
struct stu
{
char *stu[20];
}stu;
而且在c++中,定义结构体变量时是可以省略struct关键字的。
(2)定义结构体类型应该注意的地方
(1)结构体的类型定义通常都是定义在头文件中。
(2)末尾不要忘了;结尾
(3)每一个编译单元中,相同的结构体类型只能定义一次
(4)在不同的编译元中那个需要定义一个相同的结构体类型时,需
要注意,结构体的成员必须保持相同的定义顺序,否者很有可
能导致运行时错误,这主要是由于结构体的内存对齐方式比较
特别导致的。
面对这种情况时,我们尽可能将结构体的定义在头文件中,然
后在不同的转换单元中包含相同的头文件时,就可以保证在多个
转换单元中使用的是完全相同的结构体。
(5)如何给结构体变量给值(初始化和赋值)
(1)初始化和赋值的区别
有两种给支方式,初始化,还有就是赋值,初始化与赋值的却别就是,
初始化是在编译器编译程序的时候,事先就安排好了该变量或者常量的
值,运行时按照编译器的规划导入内存即可。
而赋值与初始化是不同的,赋值是在程序在内存中运行起来后,执行赋
值语句时才将值写入变量空间的。
(2)哪些情况是初始化
(1)c中的情况
(1)在定义变量的时候直接给值得就是初始化。
(2)传参是初始化。
(2)c++中的情况
保留了c中的情况。
同时c++中的构造函数也是初始化。
(3)初始化结构体
以
struct A
{
int age;
double money;
char gender;
char name[30];
};
(1)完全初始化
struct A a = {20, 300000, 'M', "zhangsan"};
struct A a1 = a;
c和c++都支持
在实际c库中,很多时候会使用宏定义来初始化结构体,比如上例
可以这么改。
#define init_A {20, 300000, 'M', "zhangsan"}
struct A a = init_A;
比如在讲linux系统编程中的线程的互斥锁和信号量时有用到过
这这样的结构体初始化方式。
(2)部分初始化
struct A b = {20, 300000, 'F'};
c和c++都支持,部分初始化必须从第一个成员开始初始化,
被初始化成员必须是连续的,初始化时不能省略前面和中间
的成员,只从从后向前省略成员。
(3)个别初始化
struct A c = {.name="wangwu", .age=30};
个别初始化只有c支持,c++并不支持,进行个别初始化时,使用.访问
需要本初始化的成员并初始化它,可以初始化任意成员,并且
没有成员数量和顺序限制。
(4)给结构体赋值
struct A
{
int age;
double money;
char gender;
char name[30];
};
(1)结构体不支持整体赋值。
struct A a = {20, 300000, 'M', "zhangsan"};
struct A b, c;
b = {30, 300000, 'F', "lisi"};//错误的赋值操作
(2)结构体只能按成员赋值
struct A a;
a.age = 20;
a.money = 300000;
a.gender = 'M';
a.name = "zhangsan";
(3)支持在结构体变量间进行整体赋值
struct A a = {20, 300000, 'M', "zhangsan"};
struct A b, c;
int main(void)
{
/* 错误做法 */
b = {20, 300000, 'M', "zhangsan"};
/* 正确做法 */
c = a;
}
(6)结构体指针
(1)结构体指针:指向结构体的指针
struct A
{
int age;
double money;
char gender;
char name[30];
}a;
struct A *p = &a;
p指向了结构体变量a。
(2)成员访问
(1)原始方式1
(*p).age = 40;
strcpy((*p).name, "zhangsan");
注意一定要使用(),因为.的结合性高于*,不加括号含义不对。
(2)改进方式
由于原始方式访问成员书写错误的概率非常高,因此使用->
进行访问。
p->age = 40;
strcpy(p->name, "zhangsan");
(7)结构体的嵌套和结构体数组
(1)结构体间的嵌套
(1)嵌套外部结构体
struct A
{
int age;
double money;
char gender;
char name[30];
}a;
struct B
{
int age;
struct A a;
struct A *p;
}a;
(3)结构体嵌套自己
结构体里面不可以可以嵌套定义自己类型结构体变量,这回导致无限去
递归初始化结构体变量,但是可以定义自己类型的指针。
struct A
{
int age;
double money;
char gender;
char name[30];
struct A a;//错误的定义
struct A *p;
}a ;
(4)内部嵌套
struct people
{
int age;
double money;
char gender;
char name[30];
struct birth_day //名字birth_day往往会被省略
{
char *year[4];
char *month[3];
char *day[3];
}birthdate;
}people;
struct people中的struct birth_day是其内部结构体定义,使用这种
内部定义的结构体的原因是,这个结构体只会被外部结构体一个人使用
,内部结构体的结构体名称往往可以省略。
在内部定义结构体的情况比较少用到。
(2)结构体数组
略
(3)对于起那套的结构体和结构体数组等的是初始化
遵循数组和结构体的初始化原则即可。
(8)结构体传参
(1)普通值传递
(1)传递整个结构体变量
这种的效率较低,很少用到这样的情况,比如对于像单片机这样
的非常底层的嵌入式计算机采用这样的传参方式是非常不合适的。
(2)传递结构体成员的值
这种情况用的也不多
(3)引用
c++还可以针对整个结构体变量和成员进行引用传递。
(2)传递地址(指针)
(1)传递整个结构体变量的地址(指针)
除了c++的引用外,这是最高效率的传参。
(2)传递结构体成员变量的地址(指针)
为了传递的高效,尽量传递指针和引用,如果不想造成实参被修改,就是用const
修饰。
(9)结构体与函数指针
(1)结构体中往往会定义很多的函数指针,主要用于构建多层结构的复杂程序。
(2)这一点在讲链表时已经使用过。
3. c++中的结构图在c结构体上做了哪些变动
(1)c++中不允许个别初始化,前面已经讲过,定义结构体变量时可以不使用struct关键字
(2)c++中引入了引用的概念,可以在结构体中定义引用成员,但是定义结构体变量
时要求必须对结构体引用成员赋初始值,同时传参也是可以使用引用。
struct A
{
int &age;
double money;
char gender;
char name[30];
};
int age = 20;
struct A a = {age, 50000, 'M', "zhangsan"};
但是这里需要注意的是,当结构体中包含有引用成员的时候,结构体变量之间
是不能够进行整体赋值的,因为引用只能被初始化,而不能被赋值。
(3)c++结构体在函数方面的扩展
我们前面说到,c中可以在结构体变量中定义函数指针成员,在c++中的结构体
又对其做了进一步的扩展,可以直接在结构体里面定义函数。
(1)在结构体里面直接定义函数
struct people
{
int age;
double money;
char gender;
char name[30];
void showme()
{
std::cout << name << std::endl;
std::cout << "性别" << gender << std::endl;
std::cout << age << "岁" << std::endl;
std::cout << money << "¥" << std::endl;
}
}p = {20, 300000, 'M', "lisi"};
int main(int argc, char **argv)
{
p.showme();
return 0;
}
(2)在结构体外部定义函数,在结构体内部声明。
#include <iostream>
#include <string>
#include <cassert>
struct people
{
int age;
double money;
char gender;
char name[30];
void showme();
}p = {20, 300000, 'M', "lisi"};
void people::showme()
{
std::cout << name << std::endl;
std::cout << "性别" << gender << std::endl;
std::cout << age << "岁" << std::endl;
std::cout << money << "¥" << std::endl;
}
int main(int argc, char **argv)
{
p.showme();
return 0;
}
(4)c++中对结构体中的成员定义了条件限制
(1)与类(class)一样三种条件限制
(1)public:公共,表示成员在结构体外部可以被访问
(2)protected:保护,成员在结构体外部不可以被访问
(3)private:私有,成员在结构体外部不可以访问
至于这三者之间的详细区别,在后序课程中有详细讲解。
(2)默认的限制
对于c语言来说成员全部都是public的,没有可以修改其访问权限的
关键字,在c++中默认情况下就是public的,但是可以人为的修改为
private和protected两种关键字。
结构体默认限制修饰符为public,这一点和class不一样,class中默
认修饰为private,这在后面会讲解到。
(3)当成员被private和protected修饰时
(1)一旦被这两个关键字修饰的话,成员不能被初始化
只能通过留下的接口函数给成员赋值。
(2)而且在结构体的外部无法被访问,只能在内访问
(3)例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <string>
#include <cassert>
struct people
{
//private:
//或者
protected:
int age;
double money;
char gender;
char name[30];
public:
void set_age(int age) {
this->age = age;
}
void set_money(int money) {
this->money = money;
}
void set_gender(char gender) {
this->gender = gender;
}
void set_name(const char name[]) {
strcpy(this->name, name);
}
void showme();
}p;
//由于成员是protected的,不能直接初始化
//struct people p = {20, 300000, 'M', "lisi"};
void people::showme()
{
std::cout << name << std::endl;
std::cout << "性别" << gender << std::endl;
std::cout << age << "岁" << std::endl;
std::cout << money << "¥" << std::endl;
}
int main(int argc, char **argv)
{
//这句话编译会不通过
//std::cout << p.money << std::endl;
p.set_age(20);
p.set_money(30000);
p.set_gender('M');
p.set_name("wangwu");
p.showme();
return 0;
}
(5)结构体也可以实现继承
(6)结构体的对齐方式
查看发给的资料。
(7)和c++中的结构体和class
实际上功能基本趋于相同,唯一的最大的不同是,权限,c++结构体中所有成员默认时public
,直通通过显示指定后,才能是其它权限。
而class中刚好相反,成员默认都是私有(隐藏),必须通过显式设置后才能便车跟其它权限。
6. 联合体
略
联合体又叫共用体,不同的成员共享
相同的内存空间,联合体空间为最大成员空间的大小,联合体变量到底存放那个成员
的值,就看最后一次给联合体赋值时,是像那个成员赋值的。
当初设计出联合体是想通过成员共享相同空间的方式去节省内存空间,但是在现在如
此发达的计算机硬件资源的情况下,已经不再需要使用联合体这样的结构来节省内存
了,但是联合体在单片机等嵌入式设备中还是有着一定的应用空间的。
基于以上理由,这里省略联合体的讲解。
5. 枚举类型
略
枚举类型实际上相当于是宏的一种扩展形式,由于很多情况我们都可以使用宏定义代替
枚举类型,因此枚举类型使用的情况很少,但是枚举类型也很简单,完全可以通过快速
自学掌握,因此这里也省去了它的讲解。