命名规则
------------只能由大小写字母与数字与下划线组成
------------变量名不能以数字开头,第一个必为字母或下划线
------------变量名区分大小写
------------变量名不能是关键字
------------变量名可以用下划线开头,但这并不好
--------------------------------------------------
#include< iostream >
int main()
{
int a;//可以
int _a;//可以
int A;//可以
int 1a;//不行,不能以非字母或下划线以外的开头
int int;//不行,不能把关键字当变量名
}
字节
布尔 bool 1
字符 char 1
整型 int 2或4
浮点 float 4
双浮点 double 8
无类型 void 0
宽字符 wchat_t 2
--------------------------------------------------
int a = 1;
float b = 64.74;
double c = 134.6473;
char d = 'h';
wchar_t e = L'a'//此a是阿尔法,我打不出来😭
bool f = false;
C++类型修饰符
------------signed
------------unsigned
------------short
------------long
-------------------------------------------------------------
字节
char 1 -128~127或0~255
unsigned char 1 0~255
signed char 1 -128~127
int 4 -2147483648~2147483647
unsigned int 4 0~4294967295
signed int 4 -2147483648~2147483647
unsigned short int 2 0~65535
signed short int 2 -32768~32757
long int 4 -9223372036854775808~9223372036854775807
signed long int 8 -9223372036854775808~9223372036854775807
unsigned long int 8 0~18446744073709551615
float 4 ~
double 8 ~
long double 16 ~
wchat_t 2或4 ~(这些不想写了,根本写不完太大了,我反正懒得写)
#include< iostream>
class B
{
int x, y;
public:
B()
{
x = y = 0;
std::cout << "创建一" << std::endl;
}
B(int i)
{
x = i; y = 0;
std::cout << "创建二" << std::endl;
}
B(int i, int j)
{
x = i; y = j;
std::cout << "创建三" << std::endl;
}
~B()
{
std::cout << "销毁" << std::endl;
}
void print();
};
void B::print()//类体外定义
{
std::cout << "x=" << x << ",y=" << y << std::endl;
}
int main()
{
B* ptr;
ptr = new B[3];//这里已经创建了对象数组,数组里面有三个对象,没有初始化就是默认构造
ptr[0] = B();
ptr[1] = B(5);
ptr[2] = B(2, 3);
for (int i = 0; i < 3; i++)
{
ptr[i].print();
}
delete[] ptr;
}
/*
创建一
创建一
创建一
创建一
销毁
创建二
销毁
创建三
销毁
x=0,y=0
x=5,y=0
x=2,y=3
销毁
销毁
销毁
我们来研究一下为什么会是这个结果
B* ptr;//对象指针
ptr = new B[3];
ptr[0] = B();
ptr[1] = B(5);
ptr[2] = B(2, 3);
创建了一个对象指针
然后给指针new,也就是堆的方式,动态分配了一个对象数组
此时数组有三个对象,且没有说明参数
所以调用的是无参构造,调用三次
紧接着
ptr[0] = B();
赋值运算是从右到左,我们看B()
创建一个对象并且不带参数,但是对象没有名字,但是也是要调用第一个构造函数
也就是无名对象,然后ptr[0]=B();
让ptr[0]指向这个无名参数
为什么会调用析构呢?
回顾我们的析构函数定义,其功能是释放对象,并不是删除内存
所以这里无名对象,其实是一个对象,虽然没有名字,但是确确实实是一个对象
这个无名对象被人抢了,所以自己就没有存在必要,就会析构
剩下的没什么了
*/
构造函数有关细节
#include< iostream>
class point
{
int x1, x2;
public:
point(int x, int y);
//这样是不行的,只有说明,没有定义
};
int main()
{
point data(4, 4);//所以在这里有参构造的时候,系统找不到有参构造函数会报错
std::cout<<"cg" << std::endl;
}
============================================================================
#include< iostream>
class point
{
int x1, x2;
public:
point(int x, int y) { };
//这样是可以的,只要有函数体了,就算定义了
};
int main()
{
point data(4, 4);//所以在这里有参构造的时候,系统找到有参构造函数
std::cout << "cg" << std::endl;
}
============================================================================
#include< iostream>
class point
{
int x1, x2;
public:
point(int x, int y) { }
//没错 我把分号去掉以后,竟然还是可以运行
};
int main()
{
point data(4, 4);
std::cout << "cg" << std::endl;
}
============================================================================
#include< iostream>//函数定义也可以在类体外进行
class point
{
int x1, x2;
public:
point(int x, int y);
};
point::point()
{
}
int main()
{
point data(4, 4);
std::cout << "cg" << std::endl;
}
============================================================================
#include< iostream>//函数定义也可以在类体外进行
class point
{
int x1, x2;
public:
point(int x, int y);
};
int main()
{
point data(4, 4);
std::cout << "cg" << std::endl;
}
//回过头总结一下
/*
如果只声明没有定义,并且是有参构造
那么在创建对象的时候会报错,因为系统找不到,并且不会退化到默认的无参构造
那可能问题来了,那我创建对象用无参构造呢?
也不行,因为你只要定义了有参构造,系统默认的无参构造就会消失
那我就要无参构造怎么办?自己加一个无参构造函数就行
例如下面的
*/
============================================================================
#include< iostream>
class point
{
int x1, x2;
public:
point(int x, int y);
point() {};
};
int main()
{
point data();
std::cout << "cg" << std::endl;
}
运算符重载
//运算符重载有两种
//第一种是重载为类的成员函数
//第二种是重载为类的友元函数
#include< iostream>
class Point
{
private:
int x, y;
public:
Point() {};
Point(int a, int b) { x = a; y = b; };
void print() { std::cout << x << "," << y << std::endl; };
Point operator+(Point& a)//本质就是个成员函数,成员函数可以访问私有变量
{
return Point(x + a.x, y + a.y);
};
//没错,重载这个+号,其实就是将两个Point对象的+法得以实现
//它的左值+右值,右值就是参数,左值呢?是this指针,这里x本质是this.x
};
int main()
{
Point p1(1, 1), p2(2, 2), p3;
p3 = p1 + p2;
p3.print();
}//输出3,3
==========================================================================
#include< iostream>
class Point
{
private:
int x, y;
public:
Point() {};
Point(int a, int b) { x = a; y = b; };
void print() { std::cout << x << "," << y << std::endl; };
friend Point operator+(Point& a, Point& b)//本质就是个友元函数,可以访问私有变量
{
return Point(a.x + b.x, a.y + b.y);
};
//没错,重载这个+号,其实就是将两个Point对象的+法得以实现
//值得注意的是,友元没有this指针,所以,它访问左值的时候
//是通过参数,也就是说友元是传两个参数
};
int main()
{
Point p1(1, 1), p2(2, 2), p3;
p3 = p1 + p2;//既然是重载,这里的+号,编译器会自动判断+左右对象来执行我们重载的+
p3.print();
}//输出3,3
//所以思考一下,p1+p2和p2+p1一样吗?是一样的,但是不可交换对于重载为成员函数来讲
//但是重载为友元函数就可以交换,因为是两个形参,不需要this指针
==========================================================================
//一元自加和自减重载
#include< iostream>
class Point
{
private:
int x, y;
public:
Point() {};
Point(int a, int b) { x = a; y = b; };
void print() { std::cout << x << "," << y << std::endl; };
void operator++()//这是重载前置++,别管我函数体内怎么写,那是我重载的操作
{
x++; y++;
};
void operator++(int)//括号里面有int,说明是重载后置++
{
x++; y++;
}
};
int main()
{
Point p1(1, 1), p2(2, 2), p3;
p1++;
++p2;
}
模板
#include< iostream>
template< class T>//函数模板
T min(T x, T y)
{
if (x < y)
return x;
else
return y;
}//这里的T实际上是占位符,系统根据传入的参数判断类型,然后用类型替换T
int main()
{
int n1 = 2, n2 = 10;
double d1 = 1.5, d2 = 5.6;
std::cout << "交小者为" << min(n1, n2) << std::endl;//min调用的时候是生成了模板函数,注意区分
std::cout << "较小者为" << min(d1, d2) << std::endl;//这个过程就是函数模板的实例化
}//模板函数的说明和定义必须是全局作用域,并且不能被说明为类的成员函数
//其次模板函数不具备类型隐式转换-》int变double,char变int
==========================================================================
#include< iostream>
using namespace std;
template < class T>//类模板
class Sample
{
T d;
public:
Sample(T i)
{
d = i;
};
void disp()
{
cout << "d=" << d << endl;
};
};
int main() // 更改返回类型为int
{
Sample < int> obj1(10);
Sample< char> obj2('a'); // 确保Sample模板类能够处理char类型的参数
Sample< const char*> obj3("China");
//这里我一开始是按照书本上敲的是 Sample< char *>obj3("China");
//一直报错,原来是"China"是字符字面量,就是不可修改的,也就是对应的应该是
//const char *类型
//字符字面量就是字面上的量,就是写的是什么就是什么
obj1.disp();
obj2.disp();
obj3.disp();
return 0; // 添加返回值
}
==========================================================================
继承与派生
#include< iostream>
using namespace std;
class x
{
protected:
int a;
public:
x()//构造函数
{
a = 10;
}
};
class x1 :public x//公有继承
{
public:
x1()
{
cout << a << endl;
}
};
class x2 :public x //公有继承
{public:
x2()
{
cout << a << endl;
}
};
class y :x1, x2
{
public:
y()
{
cout << a << endl;//这里报错,程序指明了y::a不明确
}//也就是二义性,它不知道是x1的a还是x2的a
//x1,x2的a都是通过继承x得来的,也就是说x1和x2都会得到x1关于a的一个新物体
};
void main()
{
y obj;
}
//怎么办???
======================================================================
//虚继承要求同一个子类的多个父类继承自同一个间接父类
//其它的情况,即便是虚继承也就是普通继承
#include< iostream>
using namespace std;
class x
{
protected:
int a;
public:
x()//构造函数
{
a = 10;
}
};
class x1 :virtual public x//虚公有继承
{
public:
x1()
{
cout << a << endl;
}
};
class x2 :virtual public x //虚公有继承
{
public:
x2()
{
cout << a << endl;
}
};
class y :x1, x2
{
public:
y()
{
cout << a << endl; //这里程序指明的是x的a
//其实原理是这样的,x1 ,x2中继承的x的a是指向一个的
//是公有的一个,实现方式是虚指针,指向虚表,虚指针是存放偏移量
//偏移量是a到类的偏移
//也就是说a成为了单独的一个了
//你在x1中修改a,那么x2中的a也会改变
}
};
void main()
{
y obj;
}
========================================================================
/*
派生类的构造函数执行顺序:
先执行所有基类的构造函数,再执行派生类本身的构造函数
同一层次的各基类构造函数执行顺序取决于定义派生类时所指定的各基类顺序
与派生类构造函数中所定义的成员初始化列表的各项顺序无关
包含虚基类的构造3原则:
1.虚基类的构造函数在非虚基类之前调用
2.同一层次中包含多个虚基类,按照说明的顺序调用
3.若虚基类由非虚基类派生,则仍然先调用基类构造函数
*/
#include< iostream>
using namespace std;
class base1
{
public:
base1()
{
cout << "base1" << endl;
}
};
class base2
{
public:
base2()
{
cout << "base2" << endl;
}
};
class level1 :public base2, virtual public base1//我知道可能这里会出现一点疑点
{//为什么base1没有再被构造?以为前面已经构造了,虚继承的这里就不需要再构造,共用前面的
public:
level1()
{
cout << "level1" << endl;
}
};
class level2 :public level1, virtual public base1
{
public:
level2()
{
cout << "level2" << endl;
}
};
class top :public level1, virtual public level2
{
public:
top()
{
cout << "top" << endl;
}
};
void main()
{
top obj;
}
/*打印结果
*
base1
base2
level1
level2
base2
level1
top
*/
//注意嗷,这里单纯是构造函数的顺序,析构函数相当于把这出栈
========================================================================
类模板从类模板派生
template < class T>
class A
{
};
template < class T1,class T2>
class B :public A< T2>//必须指明原本有的参数
{
};
初始化的时候也是
B< int ,int > obj;这样,后面那个int送给A当它的狗粮
========================================================================
类模板从非模板类派生
#include< iostream>
using namespace std;
class A
{
int a;
public :
A(int i)//构造函数,并且一旦你动了有参构造,系统提供的免费无参构造函数就不免费了,不给你了
{
a = i;
}
//就和我们的童年一样,一旦你想长大,并且真的长大了,没有人再呵护你的小心思了
//除非你自己呵护自己
//怎么说?
/*看下面
A()
{
scou<<"这是我自己给自己创建的无参构造"<< endl;
}
*/
};
template < class T1>
class B :public A//继承非类模板A,注意区分模板类和类模板,中国人喜欢重要的放后面,你应该懂我意思
{
T1 b;
public:
B(T1 x, int y):A(y)//这里再一次说明了,一定为没有默认构造函数的基类进行有参构造的初始化
{
b = x;
}
};//不懂也没关系,模板类是模板生成的类,类模板是一个模板
========================================================================
非类模板从类模板派生
#include< iostream>
using namespace std;
template< class T1>
class A
{
};
class B :public A< int>//必须指明实际的类型,就是int啊,char啊,这些,不允许虚空打靶
{
};//没关系,虚空打靶是我即兴想的词语。
多态性和虚函数
#include< iostream>
using namespace std;
class Point
{
int x, y;
public:
Point(int x1,int y1)
{
x = x1;
y = y1;
}
int area()
{
return 0;
}
};
class Rect :public Point
{
int l, w;
public:
Rect(int x1, int y1, int l1, int w1) :Point(x1, y1)
{
l = l1;
w = w1;
}
int area()
{
return l * w;
}
};
void fun(Point& p)//静态联编这里是默认到Point的,程序只有在运行的时候才知道是束缚到Rect上
{
cout << p.area() << endl;
}
void main()
{
Rect rec(2, 4, 10, 6);
fun(rec);
}//程序输出为0
//因为是静态联编
//C++规定动态联编是在虚函数支持下实现的
//静态联编和动态联编属于多态性
//虚函数是动态联编的基础,虚函数是成员函数,且为非static
//一个类中的成员函数被说明为虚函数,就意味着派生类中可能有不一样的定义
//让其在运行时进行关联或束定
//当派生类虚函数与基类虚函数的参数不同时,派生类的虚函数讲将丢失虚特性,变成重载函数
class a {
virtual int m(int x) = 0;// 纯虚函数,就是基类没有对虚函数进行定义,是一种特殊的虚函数
//子类定义以后,自动选择动态联编
};
//像这种带有纯虚函数的类a是抽象类
//不能建立抽象类对象
//抽象类不能用作参数类型、函数返回值、显示转换
//可以说明指向抽象类的指针和引用,此指针可以指向它的派生类,实现多态
===============================================================================
#include< iostream>
using namespace std;
class Point
{
int x, y;
public:
Point(int x1,int y1)
{
x = x1;
y = y1;
}
virtual int area()
{
return 0;
}
};
class Rect :public Point
{
int l, w;
public:
Rect(int x1, int y1, int l1, int w1) :Point(x1, y1)
{
l = l1;
w = w1;
}
virtual int area()//这里即便不说明virtual也是虚函数
{
return l * w;
}
};
void fun(Point& p)
{
cout << p.area() << endl;
}
void main()
{
Rect rec(2, 4, 10, 6);
fun(rec);
}
//输出60
//你也看到了,虚函数其实实现的是程序在何时选择执行哪个
//这也是多态的表现,用点中二的语言,不知道你看英雄联盟双城之战了没有
//杰斯穿越时空,多个时间线击杀维克托,虽然结果可能一样,但是过程绝不是一样
//也就是有多个态的表现形式,同一个物质在不同作用下的不同表现
//还有虚析构函数
======================================================================
#include< iostream>
using namespace std;
class A
{
public:
virtual void fun1(){cout << "A fun1" << endl;}
virtual void fun2() { cout << "A fun2" << endl;}
void fun3() { cout << "A fun3" << endl; }
void fun4() { cout << "A fun4" << endl; }
};
class B :public A
{
public:
virtual void fun1() { cout << "B fun1" << endl; }
virtual void fun2(int x) { cout << "B fun2" << endl; }
virtual void fun3() { cout << "B fun3" << endl; }
void fun4() { cout << "B fun4" << endl; }
};
void main()
{
A* p;
B b;
p = & b;
p->fun1();//虚函数动态联编
p->fun2();//由于基类和派生类虚函数参数类型不一致,执行静态联编
p->fun3();//基类并没有说明虚函数,子类说明了,但是子类只能和它的子类,也就是子类的子类形成动态
p->fun4();
}
/*输出
B fun1
A fun2
A fun3
A fun4
*/