C++程序设计笔记

C++程序设计笔记,第1张

C++程序设计笔记 C++程序设计笔记

机器语言:由二进制代码构成

汇编语言: 将机器指令映射为一些助记词

解释程序:翻译一条语句,计算机执行一条

编译程序:打包执行,计算机全部执行

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();
#include 
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;
};
6、析构函数
#include 
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(){
    
}
7、类的组合

两个类相互引用,前向引用声明

#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

不同枚举类无法进行比较, 因为属于不同的类型

非标准语法;请使用 “&” 来创建指向成员的指针

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;
}

没参数的话,编译器将会理解成函数指针,但函数指针需要这样写

9、友元是单向的
//如果要确保实参的值不会改变,又希望避免复制构造函数带来的开销,解决办法就是将形参声明为对象的 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。
11、指针

可以定义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;**

}

#include “stdafx.h”

Stdafx**.h是Visual **C++为每个项目配置的用来预编译的文件,在Stdafx.h文件中可以加入应用程序所需的头文件; 这东西并非必需,是 VC 自动生成用来设置预编译头文件的,对于像 MFC 这样的工程可以提高编译速度。

12、指针数组
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、构造函数 复制构造函数
#include
using 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

赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发复制构造函数的调用。例如:

Complex c1, c2; c1 = c2 ;c1=c2;

这条语句不会引发复制构造函数的调用,因为 c1 早已生成,已经初始化过了。

15、深拷贝和浅拷贝

**浅拷贝:**位拷贝,拷贝构造函数,赋值重载

多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏

**深拷贝:**每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。

#include 
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;
}

16、引用

引用是一个已存在的对象的别名

而指针是指向对象的地址

对于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;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5115281.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-17
下一篇 2022-11-17

发表评论

登录后才能评论

评论列表(0条)

保存