C++学习之路(一)

一、默认函数参数

如果函数有多个参数,将根据参数的排列顺序指定默认值。可以给任何参数指定默认值,但是有一项重要的限制:如果某个参数没有默认值,那么它前面的人任何参数都不能有默认值。下面的函数原型包含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(),并提供两个参数:

  1. 要填充的缓冲区。
  2. 最多读取多少个字符。

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这样的指针来使用变量自身。

  1. this指针的用处:
    一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。
    this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。
    在成员函数内部,我们可以直接使用调用该函数的对象的成员,而无需通过成员访问运算符来做到这一点,因为this所指的正是这个对象。任何对类成员的直接访问都被看成this的隐式使用。
    this的目的总是指向这个对象,所以this是一个常量指针,我们不允许改变this中保存的地址。
  2. 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. 宏会在预处理阶段被替换,而全局变量是在运行时;
  2. 宏定义不分配内存,全局变量定义需要分配内存;
  3. 宏不区分数据类型,它本质上是一段字符,在预处理的时候被替换到引用的位置,而全局变量区分数据类型;
  4. 宏定义之后值是不能改变的,全局变量的值是可以改变的;
  5. 宏定义只有在定义所在文件,或引用所在文件的其它文件中使用。 而全局变量可以在工程所有文件中使用,只需在使用前加一个声明。

使用宏避免多次包含
如果在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)混为一谈。后者返回类型的长度,前者指出向参数数量可变的模板传递了多少个参数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值