机器语言:由二进制代码构成
汇编语言: 将机器指令映射为一些助记词
解释程序:翻译一条语句,计算机执行一条
编译程序:打包执行,计算机全部执行
Java语言是半编译半解释的高级语言
C++是直接翻译成目的语言
0101 ^ 0000 = 0101
0101 ^ 1111 = 1010
0101 ^ 1111 ^ 1111 = 0101
0101 ^ 1111 ^ 0101 = 1111
a = 0101 = 5, b = 1111 = 13
a = a ^ b = 0101 ^ 1111 = 1010
b = a ^ b = 1010 ^ 1111 = 0101
a = a ^ b = 1010 ^ 0101 = 1111
unsigned 3 = 0011
unsigned 4 = 0100
0011 < 0100
加法运算
3 + 4 = 0011 + 0100 = 0111 = 7
5 + 10 = 0101 + 1010 = 1111 = 15
-2 + 4 = 1101 + 1011 = 1001 源码:1001 -1 = 1000 --> 取反 :0111 = 7
unsigned char usc2 = -1; // 1111 1111
printf(“usc2: %d n”, usc2); // 255
printf(“usc2: %u n”, usc2); // 255
signed char sc = -1 // 1000 0001 --> unsigned char : 128 + 1 = 129
unsigned char usc = 1 // 0000 0001
if(sc > usc){
}
-1 : 1111 1111
1、小数进制转换:2^(-1) , 2^(-2), 2^(-3) , … , 2^(-n)
0.3125 二进制转换:
0.3125 * 2 = 0.625
0.625 * 2 = 1.25
0.25 * 2 = 0.5 (取低位,即取小数点后的数进行 * 2运算)
0.5 * 2 = 1.0
所以0.3125的二进制为: 0.0101
原码,补码,反码
原码:”符号绝对值“表示编码
2、原码缺点:1、零的表示不唯一,可用加法和减法,不唯一
2、进行四则运算时符号位需单独处理且运算规则复杂
补码:
0的表示唯一
符号位可做为数值参加运算
减法运算课转换为加法运算
模数:就是一共有多少位数,比如二进制整数n位,模数为2^n
n位二进制小数的模数为2
补数:减去一个数 = 加上他的补数
反码的计算规则:1、负整数:
原码符号位不变(仍是1)
其余各位都取反
例如: x = -00110011
[x]原码 = 01110011
[x]反码 = 01001100
2、 正整数的反码就是补码
3、补码的计算规则:负数的补码 = 反码 + 1
正数的补码 = 原码
这样就可以区分0的正负表示形式, 利用了反码的符号位不变,其他位取反的 *** 作区分,要注意的是,补码 *** 作符号位有参与运算。
如果负数之和得正数,或者正数之和得负数,说明运算结果溢出
整型不能直接赋值给枚举, 但枚举可以直接赋值给整型, 因为枚举是整型的子集。
enum Weekday { SUN = 7, MON = 1, FEB, WED, THR, FRI, SAT }; enum Weekday free = SAT; Weekday res ; for (int fistday = MON; fistday <= SUN; fistday++) { res = Weekday(fistday); // 因为firstday 是int 类型, 而res是enum类型,这里相当于强制转换。 if (fistday >= free) { std::cout << "放假啦!!今天是" << res << endl; } }
c++引用一般用作函数参数来进行双向传递
inline 关键字修饰函数,可以使函数调用省去函数调用和返回 *** 作, 直接将函数嵌入调用的地方, 但前提最好是函数足够的简单,这样还可以是代码重用性提高
constexpr 修饰的函数, 在所有参数都为constexpr,默认返回值是constexpr
Microsoft visual studio F11 会进入函数执行语句, 而F10 则跳过函数,直接得到结果
4、委托构造函数Clock(int x, int y):month(x), day(y){ } Clock::Clock(0, 0){} // 可以省略重复代码 //Clock::Clock():month(0),day(0){} // 这样造成代码重复5、复制构造方法的三种调用情况
拷贝构造函数:
Array(const Array &arr); //拷贝构造函数,只为了将源实参复制给形参,直接用引用地址可以提高效率,而添加const关键字可以使得源实参不被修改。
1、用对象A, 初始化B。第一次调用复制构造函数
Point a; // 初始化一个对象 Point b(a); // 对象b使用了a对象, 开始调用复制构造函数
2、类A的对象作为函数的实参,调用复制构造函数
fun1(a);
3、函数的返回值是类的对象, 函数返回时, 调用复制构造函数,因为返回的是临时对象,而不是源对象。
b = fun2();
#include6、析构函数using namespace std; enum class Cpu_interface { Amd, Inter }; class Cpu { private: Cpu_interface cpu; unsigned int price; public: // 声明构造函数 Cpu(Cpu_interface cpu, unsigned int price); ~Cpu() { // 析构函数 cout << "cpu have been destructed..." << endl; } Cpu_interface getCpuInterface() { return cpu; } unsigned int getPrice() { return price; } }; // 定义构造函数 Cpu::Cpu(Cpu_interface c, unsigned int p) :cpu(c) { price = p; }; int main() { Cpu cpu(Cpu_interface::Amd, 9); cout << "cpuInterface: " << &Cpu::getCpuInterface << endl; cout << "price: " << &Cpu::getPrice << endl; cout << "end here ..." << endl; return 0; };
#include7、类的组合using namespace std; class Point{ public: Point(int x, int y); ~Point(); private: int xx, yy; } Point::Point(int x, int y){ xx = x; yy = y; } Point::~Point(){ }
两个类相互引用,前向引用声明
#include前向引用(注意)using namespace std; class B; // 前向引用声明 class A { public: void fun(B); }; class B { public: void fun2(A); };
但是,前向引用声明不是万能的。例如:
class B; // 前向引用声明 class A{ B b; // 初始化对象,即使使用了前向引用声明, 但是A类中初始化B类的对象还未知道B类的具体细节,引用声明未定义类体的具体内容,应该事先知道类体里面的细节,所以无法对B类进行对象初始化。 } class B{ A a; }
class B; // 前向引用声明 class A{ B* b; // 定义一个指针,而不是一个对象变量,这样就不会造成冲突,但可以通过动态分配内存去初始化对象。 //B* b = new B(); } class B{ A a; }8、枚举类定义
enum class Type:char{ General, Light, Medium, Heavy } // char 为底层类型使用枚举值
Type::General
不同枚举类无法进行比较, 因为属于不同的类型
9、友元是单向的非标准语法;请使用 “&” 来创建指向成员的指针
cout << "cpuInterface: " << &Cpu::getCpuInterface << endl; cout << "price: " << &Cpu::getPrice << endl; // 如果不加&会报错int main(void) { MyClass a; a.setvalue(); cout << "按值传递的value为:" << a.tripleByValue(参数) << endl; cout << "按引用传递的value为:" << a.tripleByReference(参数) << endl; return 0; }没参数的话,编译器将会理解成函数指针,但函数指针需要这样写
//如果要确保实参的值不会改变,又希望避免复制构造函数带来的开销,解决办法就是将形参声明为对象的 const 引用 float dist(const Point& p1, const Point& p2){ point = p1; point2 = p2; // p1 = xxx; // 错误,不能修改 } // 参数用const设为常量, 说明只能使用参数进行对读取的值, 不能对参数进行修改 *** 作。10、常函数
仅仅是为了获取成员变量的值,没有任何修改成员变量的企图,所以我们加了 const 限制
void fun() const; // 常函数保证对象不被修改 const Clazz c; // 只有常对象才能调用常函数 void Clazz::fun() const{ // const常函数保证对象不被修改 cout << "something here" << endl; } c.fun();
class A{ public: A(int i); void print(); private: const int a; static const int b; } const int A::b = 10; // 因为是第一次,这里是初始化,但是初始化后就不能再改变了 A::A(int i):a(i){} // 常函数必须通过参数列表进行初始化, 不能通过函数体内进行初始化 void print(){ cout << "this is print function" << endl; }
Point a[2] 对象数组, 有几个元素,就调用几次构造函数
最后再来区分一下 const 的位置:
- 函数开头的 const 用来修饰函数的返回值,表示返回值是 const 类型,也就是不能被修改,例如const char * getname()。
- 函数头部的结尾加上 const 表示常成员函数,这种函数只能读取成员变量的值,而不能修改成员变量的值,例如char * getname() const。
可以定义void类型的指针
void *ptr;
int i = 2; void* ptr; ptr = &i; int* ptr2 = static_cast(ptr); cout << "*ptr2 = " << *ptr2 << endl;
int a = 1; const int* ptr3; // 指向常量的指针, 不可改变为别的常量, 但可以更改地址,因为这里的指针不是常量 ptr3 = &a; int b = 3; ptr3 = &b; cout << "*ptr3 = " << *ptr3 << endl; // *ptr3 = 3; //*ptr3 = 1;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SEOxIT4M-1636171746401)(C:UsersAdministratorAppDataRoamingTyporatypora-user-images1623747565880.png)]
int* const ptr4 = &b; //ptr4 = ptr3; // 错误, 这里指针是常量, ptr4作为为指针常量,本身就不能被改变, // ptr4 = 1; // 也不能把ptr4指针修改为常量类型 int* const ptr5 = &a; //ptr4 = ptr5; // 同为指针常量也不行
class aaa
{
};
如果两次#include “aaa.h”(不见得是直接,也有可能两个不同的头文件中都包含了这个头文件)就会出错,因为相同的类不能定义两次。把aaa.h稍做修改:在编译后连接的阶段, 因为如果多个文件引用了同个头文件,连接时会出现多个文件重复出现相同内容的,含有相同头文件的代码,这样会报错, 因为每一个相同的头文件都会进行所谓的“重定义” ,
可以判断如果没有定义过,就可以用一个标志来判断是否已引用过头文件
#ifndef aaa
#define aaa
class aaa
{};
#endif
#include “iostream.h”
#define DEBUG
int main() {
#ifdef DEBUG
cout<< “Beginning execution of main()”;
#endif
return 0;**
}
12、指针数组#include “stdafx.h”
Stdafx**.h是Visual **C++为每个项目配置的用来预编译的文件,在Stdafx.h文件中可以加入应用程序所需的头文件; 这东西并非必需,是 VC 自动生成用来设置预编译头文件的,对于像 MFC 这样的工程可以提高编译速度。
int line1[] = { 1,3,4 }; int line2[] = { 3,4,5 }; int line3[] = { 3,5,6 }; int* pLine[3] = { line1, line2, line3 }; for (int i = 0; i < 3;i++) { for (int j = 0; j < 3; j++) { cout << pLine[i][j] << " "; } cout << endl; }13、函数指针(注意!)
就是函数返回值为指针的函数
int main(){ int* fun(); int* ptr = fun(); *ptr = 5; // 因为调用完函数,函数返回完返回值,函数就完成使命了,如果将函数的局部变量再次进行改变更新,会很危险,会改变函数的本身封装的特性。 } int* fun(){ int local = 0; return &local; // 函数运行结束, 变量local被释放。 }
以下作以区别:
int main(){ int* fun(); int* res = fun(); *res = 5; // 这里访问的是有效地址 delete res; // 如果这里没有释放,会造成内存泄露。 return 0; } int* fun(){ int* i = new int(); return i; // 返回的是动态分配的地址,函数结束时,动态分配的地址任然有效。 }14、构造函数 复制构造函数
#includeusing namespace std; class Complex { public: double real, imag; Complex(double r, double i) { real = r; imag = i; cout << "default constructor called" << endl; } Complex(const Complex& c) { real = c.real; imag = c.imag; cout << "Copy Constructor called" << endl; } }; int main() { Complex cl(1, 2); Complex c2(cl); //调用复制构造函数 cout << c2.real << "," << c2.imag; return 0; }
default constructor called
Copy Constructor called
1,2
15、深拷贝和浅拷贝赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发复制构造函数的调用。例如:
Complex c1, c2; c1 = c2 ;c1=c2;这条语句不会引发复制构造函数的调用,因为 c1 早已生成,已经初始化过了。
**浅拷贝:**位拷贝,拷贝构造函数,赋值重载
多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏
**深拷贝:**每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。
#include16、引用using namespace std; class IntNum { public: IntNum(int x = 0) : xptr(new int(x)) { cout << "Calling constructor" << endl; }; IntNum(const IntNum& n) :xptr(new int(*n.xptr)) { // 深拷贝, 将类成员指针xptr复制为跟形参n一样的堆内存一样的物理空间,使用动态内存分配 cout << "calling deep copy constructor.." << endl; } ~IntNum() { delete xptr; cout << "calling descontructor .." << endl; } int getInt() { return *xptr; } void setInt(const int& i) { *(this->xptr) = i; } private: int* xptr; }; int main() { IntNum a; a.setInt(123); cout << "res--> " << (new IntNum(a)) -> getInt() << endl; return 0; }
引用是一个已存在的对象的别名
而指针是指向对象的地址
对于const变量,都添加extern关键字,让每个文件都能互相访问到const变量, 防止不同文件出现同个const变量名,不同值,编译器可以通过extern关键字去检查const变量,把定义为同个变量名的变量修改为同个值。
const变量引用是常量的引用
const int var = 1; const int var2 = 2; double d = 3.14; const int& cvar3 = d; //const引用可以将非常量作为引用。 const int& cvar = var; const int& cvar = var2; // 错误,const不能被多次初始化17、虚基类
在c++中,如果一个类继承的多个类中,这多个类就继承自同个基类,这就会造成二义性,冗余,资源浪费。可以将继承同个基类的派生类添加个virtual 关键字,就是相当于在继承自基类的指针,而最后指向的都是同一个,也只有一个的基类
class base { }; class A: virtual public base { }; class B : virtual public base { }; class Derived : B { };18、虚函数
class base { public: base(); virtual ~base(); // private: int i; }; base::base() { this->i++; cout << "i++ --> " << i << endl; }; base::~base() { cout << "destructor of base called" << endl; } class Derived: public base { public: Derived(int* p); virtual ~Derived(); private: int* p; }; Derived::Derived(int* p): p(p){ cout << "constructor of Derived called" << endl; }; Derived::~Derived() { delete p; cout << "destructor of Derived called" << endl; }; int main() { int* i = nullptr; base* b = new Derived(i); // 这里因为类型是基类,所以如果没有派生类和基类定义为虚函数,会造成只调用显示的类型,并不会调用父子关系继承的类的函数 delete b; return 0; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)