C++基础知识(数据类型+逻辑判断+数组+指针+继承)

Basic Knowledge

cpp, h

头文件 h, 类,数据声明;
cpp,运行文件

Debug vs Release

Release: 发布版本

Debug: 调试版本。 占用内存大,报错信息丰富。
step over:直接执行完函数
step into: 走进函数
step out: 跳出函数,直接执行完。

数据printf

	printf("mary have %d lambs, price is %.2f",3,55.4);  //占位符
	printf("\nmary have %d lambs, price is %.2f", 3, 55.4);  // /n换行
	return 0;

数据类型

char
short
int
long
long long
float
double
bool
C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第1张图片

想要强制转换类型
(int) a

进制

1个字节为8位:
6-7 0-3
1000 1000

即为二进制: 0b10001000; 十六进制0x88.

short 为16位
15-12 11-8 7-4 3-0
1000 1000 1000 1000

十六进制:0x8888
<<左移
>>右移

八进制,十六进制

010 //8 八进制
0xa //10 十六进制
0b11 //3 二进制

\n 换行
\t 制表符4空格

左移多少位: << 8;

	unsigned char a = 0x01;
	unsigned char b = 0x02;
	unsigned int bufSize = 0;
	cout << a + b << endl;
	bufSize = a;
	bufSize = a << 8;  //相当于 *2^8
	bufSize += b;
	cout << bufSize << endl;
	return 0;

char 是有符号的
unsigned char 是无符号的,里面全是正数
1.两者都作为字符用的话是没有区别的
2.但当整数用时有区别:

常量

constat int a =7;

typedef可以简化数据类型

typedef unsigned short UINT16;

条件语句

逻辑判断

&&与
|| 或
!非

按位与 &
按位或 |
按位取非 ~

unsigned char a = 0x81;
unsigned char b = a | 0xfe;
cout<<(int) b<

如果fscore>=0, 就取fscore; 否则取0;

	int fscore = 33;
	fscore = fscore >= 0 ? fscore : 0;
	cout << fscore << endl;
	return 0;

if 判断

非零即真,非空即真

数组

一维数组

int[2] = {1,2};

二维数组

int[2][2] = {{1,2},{2,3}};

结构体

struct Student {
	char sName[255];
	int iHeight;
	float fWeight;
}

typedef struct node {
	int value;
	struct node* next = 0;
	// 构造
	node(int n) {
		value = n;
	}
}node;

int main(void){
	//初始化方法1
	Student a1 = {"Henry", 172. 66.7};
	//初始化方法2
	node* node1 = new node(1);
	// 初始化方法3
	node node2(2);
}

typedef: 重命名

指针

查看地址

查看数据的地址

int height = 177;
cout << &height<<endl;
//查看height的地址

int*

int*是一个数据类型。
指针都是4个字节。

int i = 0;
int* p = &i;   // p指向地址i
i = 3;
*p = 5;
cout << i << endl; //5
cout << *p << endl; //5
cout << &i <<endl; //006FFB80
cout << p <<endl; // 006FFB80
cout << &p <<endl; //006FFB7C

上述代码中,p为一个变量,其储存i的地址。

int **

指向指针的指针。

int i = 0, j =0;
int* pi= &i; // pi: int*
int **ppi= &p; // pp: int**
 
*pp = &j;
cout << &i <<endl; //008FFD08
cout << pi<<endl; // 008FFD08
cout << ppi<<endl; //008FFD00
地址 内容
i 008FFD08 0
j 008FFD04 0
pi 008FFD00 008FFD08
ppi 008FFCFC 008FFD00

实际上是pi储存了i的地址,ppi储存了pi的地址。
pi为返回地址中的内容,即i的内容。
**ppi即等效于
pi,因为*ppi即为取出pi的内容,也就是i的地址

申请内存

int* p = new int[1000]; //申请内存
delete[] p; //返还内存,必须得返还。

stack: 自动变量。会自动释放
全局静态区数据: 静态变量:static. 生存期从程序启动到结束
Heap/堆: 动态分配内存 malloc free© ; new, delete (c++). 必须得释放内存,否则内存泄漏, memory leakage.

引用(Reference)

自解析的指针

int i = 0;
int& j = i;
j = 3;
cout << i << endl; //3
cout << j << endl; //3
cout << &j << endl; //00EFF808
cout << &j << endl; //00EFF808

i, j 绑定了,其地址也一样。
引用必须在声明时绑定在一起,绑定后不能更改。// int& i;错误。

如果想实现在某函数中的参数进行修改,则需使用指针或者引用, 能用引用就用引用。
妙用:
如果想用函数实现,交换两变量的值
使用指针写法

void swap(int* a, int* b) {

	int t = *a;
	*a = *b;
	*b = t;
}

// 使用指针过于麻烦; 这时可以使用reference
void swap2(int& a, int& b){
	int t = a;
	a = b;
	b = t;
}
int main(void)
{
	int i = 0;
	int j = 1;
	swap(&i, &j);
	cout << i << "   " << j<< endl;
	
	swap2(i, j);
	cout << i << "   " << j<< endl;
	return 0;
}

总结: 函数中传值,传引用,传指针。
传值相当于复制,最慢, 不会改变变量。
传引用方便快。如果不想改变,可先声明const
传指针也快,麻烦。

NULL, 0 和nullptr

场景: 在构建结构体时,如果对指针next没有初始化,next会变成野指针,会导致异常。
这时需要对指针进行初始化。

	int value;
	struct node* next = 0; // NULL, nullptr也可以

NULL 和 “0”之间是等价的,因为NULL只是一个宏定义。
而nullptr是c++11中新出现的一个对象,这是一个空的指针对象。初始化一个空指针时,推荐使用。

继承

构造

构造一个派生类需要做以下三部分工作:

从基类接受成员。

派生类会把基类的全部成员(不包括构造函数和析构函数)接收过来,不能选择接收其中的一部分。

调整从基类接受的成员。

在派生类中可以改变基类成员在派生类中的访问属性,这就是下面我们要将的指定继承方式来实现的。
如果在派生类中声明了一个与基类成员相同名字的函数,派生类的新成员会覆盖基类的同名成员。

在声明派生类时增加的成员

详见多继承中的例子

析构

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

class A
{
    int value;
    A(int a)
    {value = a;}

	~A(){
	cout << "~A is excuted" << endl;}
};

单继承

C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第2张图片
代码示例

class<派生类名>:<继承方式><基类名>
{
<派生类新定义成员>
};
// example
class Base
{};
class Derive:public Base//公有继承
{};
class Derive2:protected Base //保护继承
{};
class Derive3:private Base//私有继承
{};

多继承

代码示例

class<派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,{
    <派生类新定义成员>
};
//example
class A
{
    int a;
    void a()
    {}
};
class B
{
    int b;
    void b()
    {}
};
class C :public A, public B
{
    int c;
    void c()
    {}
};
————————————————
版权声明:本文为CSDN博主「长着胡萝卜须的栗子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lixungogogo/article/details/51118524

C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第3张图片

虚继承

转载自: https://www.cnblogs.com/lovemee/p/10533324.html
C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第4张图片
在多重继承中,如果发生了如:类B继承类A,类C继承类A,类D同时继承了类B和类C。最终在类D中就有了两份类A的成员,这在程序中是不能容忍的。当然解决这个问题的方法就是利用虚继承。

class<派生类名>:virtual <继承方式1><基类名1>, virtual <继承方式2><基类名2>,{
    <派生类新定义成员>
};
class B:virtual public A{}
class C :virtual public A{}
class A{
public:
    int t;
    A(int a)
    {
        t = a;
    }
    void fun();
};

class B:virtual public A
{
public:
    B(int a, int b) :A(a+10)
    {
        t1 = b;
    }
    ~B();
    int t1;
};

class C :virtual public A
{
public:
    C(int a,int c):A(a+20)
    {
        t2 = c;
    }
    ~C();
    int t2;
};

class D :public B,public C
{
public:
    D(int a,int b,int c,int d) :B(a,b),C(a,c),A(a){}//在此必须要给虚基类传参
    ~D();
};

注意:C++编译系统在实例化D类时,只会将虚基类的构造函数调用一次,忽略虚基类的其他派生类(class B,class C)对虚继承的构造函数的调用,从而保证了虚基类的数据成员不会被多次初始化。
即: 上述代码示例中,实例化D时我们给a传入一个1,将忽略B,C类中的a,直接去A中的a。

虚函数

转载自: https://blog.csdn.net/weixin_43329614/article/details/89103574

有助于识别对象具体类型,如后面向上类型转换时,可以识别对象的具体类型。

虚函数的实现是由两个部分组成的,虚函数指针与虚函数表
虚函数指针:从本质上来说就只是一个指向函数的指针,与普通的指针并无区别。它指向用户所定义的虚函数,具体是在子类里的实现,当子类调用虚函数的时候,实际上是通过调用该虚函数指针从而找到接口。
下面通过代码来详细介绍。

class Base
{
    public:
    virtual void f(){cout<<"Base::f"<<endl;}
    virtual void g(){cout<<"Base::g"<<endl;}
    virtual void h(){cout<<"Base::h"<<endl;}
};`

C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第5张图片

class Derived:public Base
{
	public:
    virtual void f(){cout<<"Derived::f"<<endl;}
    virtual void g1(){cout<<"Derived::g1"<<endl;}
    virtual void h1(){cout<<"Derived::h1"<<endl;}
}

f被重写,会覆盖父类虚函数中的虚函数其指针,而子类自己的虚函数将跟在表后。
C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第6张图片

多继承时的虚函数

无虚函数覆盖
C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第7张图片
而当多重继承的时候,表项将会增多,顺序会体现为继承的顺序,并且子函数自己的虚函数将跟在第一个表项后。

有虚函数覆盖
C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第8张图片
C++基础知识(数据类型+逻辑判断+数组+指针+继承)_第9张图片

  	   Derive d;
         Base1 *b1 = &d;
         
          Base2 *b2 = &d;

          Base3 *b3 = &d;

          b1->f(); //Derive::f()

          b2->f(); //Derive::f()

          b3->f(); //Derive::f() 

          b1->g(); //Base1::g()

          b2->g(); //Base2::g()

          b3->g(); //Base3::g()

三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了.

纯虚函数

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 =0:
相当于java中的Abstract.

virtual void funtion1()=0

含有虚函数的类为抽象类,不能new 实例,与Java中一样。
继承类必须重写全部的纯虚函数,才能被实例化。

using std::string;  
//define interface  
class Person  
{  
public:  
    Person():m_StrName("###") //成员列表初始化参数  
    {};  
    virtual ~Person(){};  
    virtual void Eat()=0;//人需要吃东西  
    virtual void Sleep()=0;//人需要睡觉  
    virtual void SetName(const string strName)=0;//人都有名字  
    virtual string GetName()=0;//获取名字  
    virtual void Work()=0;//人可能要有工作  
private:  
    string m_StrName;  
};  
//实现接口  
//实现接口是通过派生类实现的,每个派生类依据自身特点,可以获取同一接口的不同实现  
//也就是所谓的多态  
class Student:public Person  
{  
public:  
    Student():m_strName("***")  
    {};  
    ~Student()  
    {};  
    void Eat();  
    void Sleep();  
    void SetName(const string strName);  
    string GetName();  
    void Work();  
private:  
    string m_strName;  
};  
#endif  

你可能感兴趣的:(c++,c++)