头文件 h, 类,数据声明;
cpp,运行文件
Release: 发布版本
Debug: 调试版本。 占用内存大,报错信息丰富。
step over:直接执行完函数
step into: 走进函数
step out: 跳出函数,直接执行完。
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
想要强制转换类型
(int) a
6-7 | 0-3 |
---|---|
1000 | 1000 |
即为二进制: 0b10001000; 十六进制0x88.
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;
非零即真,非空即真
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*是一个数据类型。
指针都是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 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.
自解析的指针
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
传指针也快,麻烦。
场景: 在构建结构体时,如果对指针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;}
};
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
转载自: https://www.cnblogs.com/lovemee/p/10533324.html
在多重继承中,如果发生了如:类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;}
};`
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被重写,会覆盖父类虚函数中的虚函数其指针,而子类自己的虚函数将跟在表后。
无虚函数覆盖
而当多重继承的时候,表项将会增多,顺序会体现为继承的顺序,并且子函数自己的虚函数将跟在第一个表项后。
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