文章目录
- 类与对象简介
- 类与结构的区别
- 定义成员函数
- 继承
- 继承小总结
- [C++中::和:, .和->的作用和区别](https://zhuanlan.zhihu.com/p/165992745)
- 符号::和:的作用和区别
- ::
- :一般用来表示继承
- 符号.和->的作用和区别
- #include
- #include""和#include<>区别
- .h文件(声明)和.cpp文件(定义)
- 头h文件中的保护措施(防止同一文件多次包含同一头文件)
- h文件和cpp文件实战
- h文件
- #ifndef...#endif

类与对象简介
类需要实例化对象才能起作用。
样例:
关键字class表示类,一般首字符用大写字母表示,以示与对象名的区别。关键字public和protected(或private)表示访问控制。在类中说明的,要么是数据成员,要么是成员函数。它们或者说明为public的,或者说明为protected的,或者说明为private的。类具有封装性,它可以说明哪些成员是public的,哪些不是。说明了protected的成员,外部是不能访问的.
但是可以通过类的public成员函数间接地访问。
类与结构的区别
- C++中,结构是用关键字struct声明的类,默认情况下其成员是公共(public)的。
- C++中,默认情况下类(class)定义中的成员是private的。
定义成员函数
∷叫作用域区分符,指明一个函数属于哪个类或一个数据属于哪个类。∷可以不跟类名,表示全局数据或全局函数(即非成员函数)
类内定义成员函数
类外定义成员函数
类名加在成员函数名之前而不是加在函数的返回类型前
将类定义和其成员函数定义分开,是目前开发程序的通常做法。我们把类定义(头文件)看成类的外部接口,类的成员函数定义看成类的内部实现。将类拿来编制应用程序时,只需类的外部接口(头文件)。这和我们使用标准库函数的道理是一样的,即只需包含某函数声明的头文件。因为类定义中全部包含了类中成员函数的声明。
继承
C++的三种继承方式详解
C++继承详解
C++继承祥祥祥解
在实际运用中一般使用的都是public继承,几乎很少使用protected/private继承,也不提倡使用protected/private继承,因为protected/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
1.基类private成员在派生类中无论以什么方式继承都是不可见的。(注意:这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它)
2.如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。
3.上述表格实质有个规律:基类的私有成员在派生类中都是不可见的,基类的其他成员在派生类中的访问方式为Min(成员在基类的访问限定符,继承方式)(这里的大小关系为public > protected > private)。
样例
继承小总结
- 派生类继承基类的方式,决定基类中成员在派生类中的最高权限(高于则降权限)。
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
- 通过派生类的对象访问从基类继承的成员,只能访问public成员。
若派生类想调用基类不可访问成员,只能通过调用基类可访问成员方法间接调用基类不可访问成员。
C++中::和:, .和->的作用和区别
符号::和:的作用和区别
::
- ::是作用域运算符,A::B表示作用域A中的-名称B,A可以是名字空间、类、结构;
- 类作用域操作符
- “::”指明了成员函数所属的类。如:M::f(s)就表示f(s)是类M的成员函数。
作用域,如果想在类的外部引用静态成员函数,或在类的外部定义成员函数都要用到。
使用命名空间里的类型或函数也要用到(如:std::cout, std::cin, std::string 等等) - 表示“域操作符”
例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时, 就要写成void A::f(),表示这个f()函数是类A的成员函数。 - 表示引用成员函数及变量,作用域成员运算符
例:System::Math::Sqrt() 相当于System.Math.Sqrt()
:一般用来表示继承
- 表示结构内位域的定义(即该变量占几个bit空间)
typedef struct _XXX{
unsigned char a:4;
unsigned char c;
};XXX
- 在构造函数后面,冒号起分割作用,是类给成员变量赋值的方法,初始化列表,更适用于成员变量的常量const型。
构造函数,就是与类同名的函数,它与普通函数的区别在于,它没有返回类型。在构造函数后面紧跟着冒号加初始化列表,各初始化变量之间以逗号(,)隔开。
例如:
class myClass
{
public :
myClass();// 构造函数,无返回类型,可以有参数列表,这里省去
~myClass();// 析构函数
int a;
const int b;
}
myClass::myClass():a(1),b(1)// 初始化列表
{
}
关于“:”冒号的用法的说明:
- 初始化列表的作用相当于在构造函数内进行相应成员变量的赋值,但两者是有差别的。
在初始化列表中是对变量进行初始化,而在构造函数内是进行赋值操作。两都的差别在对于像const类型数据的操作上表现得尤为明显:const 类型的变量必须在定义时进行初始化,而不能对const型的变量进行赋值,因此const类型的成员变量只能(而且必须)在初始化列表中进行初始化,如:
myClass::myClass()
{
a = 1;// 没错,效果相当于在初始化列表中进行初始化
b = 1;// 出错,const变量不能进行赋值操作;
}
2) 初始化的顺序与成员变量声名的顺序相同。
先看一下下面的程序:
myClass::myClass():b(1),a(b)
{
}
这样的执行结果a,b各是多少呢?b=1,a=1?不是,b=1而a是个随机数。这一点是相当重要,一般在初始化列表中进行初始化时,初始化的顺序应与声明的顺序保持一致,防止出现不必要的错误。
- public:和private:后面的冒号,表示后面定义的所有成员都是公有或私有的,直到下一个"public:”或"private:”出现为止。"private:"为默认处理。
- 类名冒号后面的是用来定义类的继承。
class 派生类名 : 继承方式 基类名
{
派生类的成员
};
这里的冒号起到的就是声名基类的作用,在基类类名前面可以加public/private/protected等标签,用于标识继承的类型,也可以省略, 省略的话,用class定义的类默认为private,用struct定义的类默认为public。
与初始化列表一样的,这里也可以声名多个基类,各基类之间用逗号(,)隔开。
- 条件语句(? : )
与?构成条件语句,作用相当于if else,如下;
int a,b,c;
a=3;
b=2;
c=a>b?a:b;// 如果a>b成立,则反a赋给c,否则把b赋给c
条件语句的结构为:
条件表达式?表达式1:表达式2
当条件表达式为true时,表达式的值为表达式1的值,否则为表达式2的值。
注意:
1)?:可以嵌套,但不推荐使用(难懂),例如:
int max = i>j ? i>k ? i : k : j>k ? j : k;
2)?:具有很低的优先级,例如:
int i = 3;
int j = 2;
cout << i>j?i:j;// 出错,<<比>具有更高的优先级,执行顺序为 ((cout<<i)>j)?i:j,相当于是比较cout<<i与j的大小,然后根据比较结果决定表达式值为i或j,这 显然要出错的,cout<<i的值是cout,不能跟整型数j进行比较。
cout << (i>j)?i:j;//输出1或0,相当于(cout<<(i>j))作为判决条件,来决定表达式的值为i或j,而 cout<<(i>j),i>j则输出1否则0,然后再将(cout<<(i>j))作为?:的条件,如果 cout正确执行则为1(true),否则为0(false),以此决定表达式值为i或j
cout <<(i>j?i:j);//i>j则输出i,否则输出j,表达式值为true如果cout正确执行,否则为false
- 语句标签
通常跟goto配合使用,如:
step1: a = f1();
....
goto step1;
这种作法也不是很推荐,原因在于它破坏了语句的顺序执行;
- switch语句中case后。
符号.和->的作用和区别
A.B则A为对象或者结构体; 点号(.):左边必须为实体。
A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针; 箭头(->):左边必须为指针;
->的优先级比较高==(*指针).
class A
{
public:
int a = 0;
};
int main()
{
A b;
A *p = &b;
b.a; //类类型的对象访问类的成员
p->a; //类类型的指针访问类的成员
}
#include
C++头文件和cpp文件的原理
#include 是一个来自C语言的宏命令,它在编译器进行编译之前,即在预编译的时候就会起作用。#include的作用是把它后面所写的那个文件的内容,完完整整地、一字不改地包含到当前的文件中来。值得一提的是,它本身是没有其它任何作用与副功能的,它的作用就是把每一个它出现的地方,替换成它后面所写的那个文件的内容。简单的文本替换,别无其他。
#include 一个文件(无论.h或是.cpp),仅仅是将文件中的代码复制一份到你的代码之中,头文件(.h)仅在编译过程中的预处理中起到作用。
#include""和#include<>区别
二者区别如下(开始搜索的位置不同):
1、“”是表示从当前目录开始搜索,然后是系统目录和PATH环境变量所列出的目录。
2、<>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不搜索当前目录。
正常情况下,自行定义的头文件应该用"",因为这些文件放在工程目录(也就是编译器的当前目录)下,而不是放在公共头文件目录下。
而系统提供的头文件,比如库函数的头文件,可以用<>。
这样效率最快。
.h文件(声明)和.cpp文件(定义)
关于C++ .h文件和.cpp文件的知识梳理
通常,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。
- .cpp文件被称作C++源文件,里面放的都是C++的源代码
- .h文件则被称作C++头文件,里面放的也是C++的源代码,头文件不用被编译
注意这里提到了两个概念,一个是“声明”,一个是“定义”。简单地说:
- “声明”则只是声明这个符号的存在,即告诉编译器,这个符号是在其他文件中定义的,我这里先用着,你链接的时候再到别的地方去找找看它到底是什么吧。声明的时候就只需要写出这个符号的原型了。
- “定义”就是把一个符号完完整整地描述出来:它是变量还是函数,返回什么类型,需要什么参数等等。定义的时候要按C++语法完整地定义一个符号(变量或者函数)
需要注意的是,一个符号,在整个程序中可以被声明多次,但却要且仅要被定义一次。
仅在.h 文件中进行变量和函数的声明而不进行定义(存在一些特例,比如class 的内敛成员函数,模板函数), 因为函数与变量的声明可以进行任意多次,而定义只能有一次。
头文件不用被编译。我们把所有的函数声明全部放进一个头文件中,当某一个.cpp源文件需要它们时,它们就可以通过一个宏命令#include包含进这个.cpp文件中,从而把头文件的内容合并到.cpp文件中去。当.cpp文件被编译时,这些被包含进去的.h文件的作用便发挥了。
函数声明和定义的分离十分简单,我们只需要在.h文件中写出函数返回值类型函数名、接受的参数,再到.cpp文件中去写函数的具体实现就行了。而变量的声明和定义的分离就要靠关键字extern 了。extern 主要有两个作用,其中一个是对指定单元强制使用C 的编译器进行编译,另一个便是声明变量的作用范围的关键字,其声明的函数和变量可以在本编译单元或其他编译单元中使用。
总结:当你需要定义一个变量给所有include 该头文件的单元共同使用时,你应该在.h文件中使用extern 关键字,表示该变量将在include这个头文件的编译单元中使用(即进行“声明”)。同时另外新建一个.cpp文件对该头文件中声明的变量进行统一定义(定义变量的文件实际上可以是任意include该头文件的.cpp文件,但为了避免混乱,我们通常约定是在和头文件同名的.cpp文件中进行定义,可以认为是一种规范)。
头h文件中的保护措施(防止同一文件多次包含同一头文件)
考虑一下,如果头文件中只包含声明语句的话,它被同一个.cpp文件包含再多次都没问题——因为声明语句的出现是不受限制的。然而,上面讨论到的头文件中的三个例外也是头文件很常用的一个用处。
那么,一旦一个头文件中出现了上面三个例外中的任何一个,它再被一个.cpp包含多次的话,问题就大了。因为这三个 例外中的语法元素虽然“可以定义在多个源文件中”,但是“在一个源文件中只能出现一次”。
设想一下,如果a.h中含有类A的定义,b.h中含有类B的定义,由于类B的定义依赖了类A,所以b.h中也#include了a.h。现在有一个源文件,它同时用到了类A和类B,于是程序员在这个源文件中既把a.h包含进来了,也把b.h包含进来了。
这时,问题就来了:类A的定义在这个源文件中出现了两次!于是整个程序就不能通过编译了。你也许会认为这是程序员的失误——他应该知道b.h包含了a.h——但事实上他不应该知道。
使用#define
配合条件编译可以很好地解决这个问题。在一个头文件中,通过#define
定义一个名字,并且通过条件编译#ifndef...#endif
使得编译器可以根据这个名字是否被定义,再决定要不要继续编译该头文中后续的内容。这个方法虽然简单,但是写头文件时一定记得写进去。
h文件和cpp文件实战
.h文件中写出函数返回值类型、函数名、接受的参数,再到.cpp文件中去写函数的具体实现就行了
.cpp文件实现.h文件的声明
cpp文件实际上可以是任意include该头h文件的.cpp文件,但为了避免混乱,我们通常约定是在和头文件同名的.cpp文件中进行定义,可以认为是一种规范。
h文件
h:类声明,方法声明,const,define变量声明
注意:#ifndef...#endif
#ifndef…#endif
#ifndef…#endif属于预处理功能中三种(宏定义define ,文件包含include和条件编译)中的第三种—-条件编译
c++中的# ifndef 与#endif
#ifndef X标识符 //先测试x是否被宏定义过,如果被定义则返回假,如果没有被定义则返回真。
#define X标识符
// 如果X没有被宏定义过,定义为X,并执行该语句
#endif //终止if
在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前面加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef _STDIO_H
#define _STDIO_H
......
#endif