一、默认函数参数
如果函数有多个参数,将根据参数的排列顺序指定默认值。可以给任何参数指定默认值,但是有一项重要的限制:如果某个参数没有默认值,那么它前面的人任何参数都不能有默认值。下面的函数原型包含4个参数:long set4DPoint(int x, int y, int z, int t);
不能对其做如下修改:long set4DPoint(int x, int y, int z = 1, int t);
原因是参数 t 没有默认值。下面是一个合法的原型:
long set4DPoint(int x, int y, int z =1, int t =2;
可以这样调用函数:set4DPoint(130, 85);
得到的结果是130, 85, 1, 2。
二、高级for循环
for循环功能强大而灵活。经常需要初始化多个变量、检查符合逻辑表达式并执行多条语句。如果初始化和操作部分包含多条语句,就必须使用逗号分隔,如下例所示:
for (int x = 0, y = 0; x < 10; x++, y++) {cout << x * y << endl;}
for循环的每个部分都可以空着,但是分隔各个部分的分号必不可少,有些部分可不包含任何代码,如下所示:
int x = 0; int y = 0; for ( ; x < 10;x++, y++) {cout << x * y << endl;}
三、数组
要获悉数组包含多少个元素,可使用c++内置函数sizeof()
const int size = sizeof(post) / sizeof(post [0]);
如果是直接sizeof(post)则显示全部的位数: 4 x 个数。
赋予数组初值时必须用大括号{}。
char yum[ ] = “Zombie Eaat Brains”
在输入字符数组时,如果用cin来接收来自键盘的内容,会存在两问题:首先,如果用户输入的字符数超过了缓冲区的长度,cin写入时将跨越缓冲区边界,导致程序不能正确运行,还导致安全问题。其次,如果用户输入了空格,cin认为字符串到此结束了。
所以要调用cin对象的方法getlin(),并提供两个参数:
- 要填充的缓冲区。
- 最多读取多少个字符。
cin.getline(yum, 18);
调用这个方法时,还可以提供第三个参数——终止输入的分隔符:
cin.getline(yum, 18, ' ');
strcpy(string2, string1) 字符串2复制字符串1。
四、指针
栈区(stack)— 由编译器自动分配释放 ,存放函数参数值,局部变量值等。其操作方式类似于数据结构中栈。
堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中堆是两回事
类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。
字符 \\
用于显示反斜杠;
字符\"
用于显示双引号;
字符\'
用于显示单引号。
指针是一种特殊的变量,用于储存对象在内存中的地址。声明指针时,指定它将指向的变量类型,这告诉编译器如何处理指针指向的内存单元。指针本身包含一个地址。
声明指针时就应该将其初始化,如果不知道应该给指针指定什么值,就指定空指针:
int *p = nullptr;
要分配堆中的内存,可以使用关键字new
,并在它后面指定要为之分配内存的对象类型,让编译器知道需要多少内存。关键字new
返回一个内存地址,必须将其赋给指针。
unsigned short int *pPoint = new unsigned short int
使用完分配的内存区域后,必须对指针调用delete
,将内存归还给堆。delete pPoint
对于在栈中创建的对象,使用句点运算符(.)来访问其成员数据和成员函数;要访问堆中的对象,必须对指针解除引用,并对指针指向的对象使用句点运算符。因此,要访问成员函数A,可编写如下函数:
//classname *A = new classname
其中有成员函数B。
(*A).B
其中的括号是用于确保对A解除引用再访问B。但是这样比较繁琐,所以使用指向运算符 -> 。对于堆中的对象,使用->。
const int /*pOne;
pOne是指向整型常量的指针,即使用该指针不能修改它指向的值,意味着不能这样编写
*pOne = 5;
int const /*pTwo;
pTwo是指向整型的常量指针,可以修改指向的整型变量,但pTwo不能指向其他变量。不能给常量指针重新赋值,意味着不能编写下面的代码
:pTwo = &x
const int /* const pThree;
pThree 是一个指向整型常量的常量指针,不能修改它指向的值,也不能让它指向其他变量。
分辨的方法:在星号*右边画一条竖线,如果const在竖线左边,则意味着指向的对象是常量,如果const在竖线右边,则意味着指针本身是常量。
this指针:
通常在class定义时要用到类型变量自身时,因为这时候还不知道变量名(为了通用也不可能固定实际的变量名),就用this这样的指针来使用变量自身。
- this指针的用处:
一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。
在成员函数内部,我们可以直接使用调用该函数的对象的成员,而无需通过成员访问运算符来做到这一点,因为this所指的正是这个对象。任何对类成员的直接访问都被看成this的隐式使用。
this的目的总是指向这个对象,所以this是一个常量指针,我们不允许改变this中保存的地址。 - this指针的使用:
一种情况就是,在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this;另外一种情况是当参数与成员变量名相同时,如this->n = n (不能写成n = n)。
#include <iostream>
using namespace std;
class A
{
public:
int get() const{return i;}
void set(int x){this->i=x;cout<<"this指针保存的内存地址为:"<<this<<endl;}
private:
int i;
};
int main()
{
A a;
a.set(9);
cout<<"对象a所在的内存地址为:"<<&a<<endl;
cout<<"对象a所保存的值为:"<<a.get()<<endl;
cout<<endl;
A b;
b.set(999);
cout<<"对象b所在的内存地址为:"<<&b<<endl;
cout<<"对象b所保存的值为:"<<b.get()<<endl;
return 0;
}
通过这个输出结果,我们可以看到,对象a的内存地址和this指针的一模一样(都是0017F7E8);而当运行到对象b的时候,它的内存地址又和它所对应的this指针指向的内存地址一模一样了(都是0017F7DC)。这就说明了this指针变量记录的是当前对象的内存地址,即this指针指向当前的对象!
在程序的第8行,我们就用了this指针的这个属性,即:this->i=x;这句话就表示把x的值赋值给当前的对象的私有成员函数i。
五、引用
引用是一个别名,创建引用时,使用另一个对象(目标)的名称来初始化它,从此以后,该引用就像是目标的另一个名称,对引用执行的任何操作实际上针对的就是目标。
指针和引用的区别:指针是存储另一个对象的地址的变量,而引用是对象的别名。
int &rSomgRef = someInt;
创建引用需要指定目标对象的类型、引用运算符(&)和引用名。
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
int main()
{
int intOne;
int &rSomeRef = intOne;
intOne = 5;
cout << "intOne : " << intOne << endl;
cout << "rSonmRef : " << rSomeRef << endl;
cout << "&intOne : " << &intOne << endl;
cout << "&rSomeRef : " << &rSomeRef << endl;
int intTwo = 8;
rSomeRef = intTwo;
cout << "intOne : " << intOne << endl;
cout << "intTwo : " << intTwo << endl;
cout << "rSonmRef : " << rSomeRef << endl;
cout << "&intOne : " << &intOne << endl;
cout << "&intTwo : " << &intTwo << endl;
cout << "&rSomeRef : " << &rSomeRef << endl;
return 0;
}
创建了新变量intTwo,并将其初始化为8;试图给rSomeRef重新赋值,试其成为成为intTwo的别名,但结果并非如此。
实际上,rSomeRef还是intOne的别名,因此该复制语句与下述语句等价:
intOne = intTwo
六、初始化对象
this指针:
this指针指向用来调用成员函数的对象(this被作为隐藏参数传递给方法)。
构造函数由两部分组成:初始化部分和函数体
可在初始化部分设置成员变量,也可以在构造函数的函数体内设置。
Tricycle :: Tricycle():
speed(5), wheelSize(12)
{
}
要在构造函数的初始化部分赋值。可在用于构造函数参数列表括起的右括号后面添加一个冒号,再列出要初始化的变量名。在变量名后面添加一对括号,并在括号内包含用于初始化成员变量的表达式。多个成员变量间用逗号隔开。
复制构造函数:https://www.cnblogs.com/alantu2018/p/8459250.html
内联函数:
int max(int a, int b)
{
return a > b ? a : b;
}
为什么一个小的操作定义一个函数的好处有:
① 阅读和理解函数 max 的调用,要比读一条等价的条件表达式并解释它的含义要容易得多。
② 如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多。
③ 使用函数可以确保统一的行为,每个测试都保证以相同的方式实现。
④ 函数可以重用,不必为其他应用程序重写代码。
虽然有这么多好处,但是写成函数有一个潜在的缺点:调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作:调用前要先保存寄存器,并在返回时恢复,复制实参,程序还必须转向一个新位置执行。
C++中支持内联函数,其目的是为了提高函数的执行效率,用关键字 inline 放在函数定义(注意是定义而非声明)的前面即可将函数指定为内联函数,内联函数通常就是将它在程序中的每个调用点上“内联地”展开,假设我们将 max 定义为内联函数:
inline int max(int a, int b)
{
return a > b ? a : b;
}
则调用: cout<<max(a, b)<<endl;
在编译时展开为: cout<<(a > b ? a : b)<<endl;
从而消除了把 max写成函数的额外执行开销。
定义在类声明之中的成员函数将自动地成为内联函数
class A
{
public:
void Foo(int x, int y) { ... } // 自动地成为内联函数
}
(构造函数用于初始化成员变量的值)
共用体:
共用体是一种特殊的类,每次只有一个非静态数据成员处于活动状态。因此,共用体与类一样,可包含多个数据成员,但不同的是只能使用其中一个。
union UnionName
{
Type1 member1;
Type2 member2;
...
Type1NmemberN;
};
七、多继承
什么时候需要用到多继承?
- 只要你遇到的问题无法只用一个“是一个”关系来描述的时候,就是多继承出场的时候。
- 举个栗子:在学校里有老师和学生,他们都是人(Person),我们可以用“老师是人”和“学生是人”语法来描述这种情况。
- 从面向对象编程角度上来看,应该创建一个名为Person的基类和两个名为Teacher和Student的子类,后两者是从前者继承来的。
要是有学生当了助教(既是老师也是学生),也就是同时存在着两个“是一个”关系。就需要用到多继承。
class TeachingStudent : public Student, public Teacher{...}
在派生类中调用基类的方法:
在调用的时候使用作用于解析运算符(::)
mydinner.Fish::Swim()
八、多态
new和delete
用new申请一块动态内存时,用完后要用delete删除。
company *Company = new company("Apple");
Company -> printInfo();
delete Company;
Company = NULL;
九、宏
使用#define 定义常量:
#define indentifier value
宏和全局变量的区别:
- 宏会在预处理阶段被替换,而全局变量是在运行时;
- 宏定义不分配内存,全局变量定义需要分配内存;
- 宏不区分数据类型,它本质上是一段字符,在预处理的时候被替换到引用的位置,而全局变量区分数据类型;
- 宏定义之后值是不能改变的,全局变量的值是可以改变的;
- 宏定义只有在定义所在文件,或引用所在文件的其它文件中使用。 而全局变量可以在工程所有文件中使用,只需在使用前加一个声明。
使用宏避免多次包含:
如果在1.h里声明了一个类,而这个类将2.h中声明的类作为其成员函数,则需要在1.h中包含2.h。如果设计非常复杂,则在2.h中也需要包含1.h。
解决办法:
#ifndef 1_H
#defin 1_H
。。。。。。
。。。。。。
#endif
预处理器首次处理1.h并遇到#ifndef后,发现宏HEADER1_H还没有定义,因此继续处理。如果已经定义,则不重复定义。
可以使用宏定义一些简单的函数。
#define MIN(a,b) (( (a) < (b) ) ? (a) : (b))
要用括号把每个变量括起来。
十、模板
模板无疑是c++语言中最强大却最少被使用的特性之一。
模板声明语法:
template <typename T1, typename T2>
模板函数:
template <typename Type>
const Type& GetMax(const Type& value1, const Type& value2)
{
....
....
}
调用模板函数可以不用指定类型,但是调用模板类,则必须显式地指定类型。
模板类:
template <typename T1, typename T2>
class MyTemplate
{
private:
T1 member1;
T2 member2;
public:
T1 GetObj1() {return member1; }
};
参数可变模板(c++ 14)
int arrNums[sizeof ... (Rest)];
*不要吧sizeof…()和sizeof(Type)混为一谈。后者返回类型的长度,前者指出向参数数量可变的模板传递了多少个参数。