C++笔记8.21

new&delete
C中动态内存分配用malloc,free
new int ; new Stash ; new int[10] 分配空间(通过指针访问),对类调用构造函数
delete p ; delete[] p;
new带方括号,则delete也带
int * psome = new int[10] ; delete [] psome; 不带方括号,只有第一个对象会被析构
int table会记录new的内存首地址和大小

int *p = new int;
int *a = new int[10];
Student *q=new
	Student();
Student *r=new
	Student[10];
a++;delete[] a; //产生错误,找不到
class A {
public:
	int i;
	A() { i = 0;  cout << "A::A()" << endl; }
	~A() { cout << "A::~A() i=" <<i<< endl; }

	void set(int i) { this->i = i; }
	void f() { cout << "f()" << endl; }
};
int main() {
	A* p = new A[10];
	for (int i = 0; i < 10; i++) {
		p[i].set(i);
	}
	delete []p;
	return 0;
}

在这里插入图片描述

不要用delete去删除不是new出来的空间;
不要用delete两次删除同一个空间;
有无方括号相对应
可以delete一个null pointer; if§ delete§; 内存泄露

访问限制
public
private 类的成员函数可以访问,其他对象的成员函数也可以访问
protected 类自己以及子子孙孙可以访问
访问限制仅在编译时刻,.o文件是一样的
Friends ,声明后别人(函数,类,成员函数)可以访问自己private的东西
类中缺省的private, struct缺省的是public

struct X;  //前项声明
struct Y {
	void f(X*); //f(X c)不行
};

struct X {
private:
	int i;
public:
	void init();
	friend void g(X*, int);
	friend void Y::f(X*);
	friend struct Z;
	friend void h();
};

void X::init() {
	i = 0;
}

void g(X* x, int i) {
	x->i = i;
}

初始化列表 (推荐)

A():p(0){};
class Point{
private:
	const float x,y;
	Point(float xa=0.0,float ya=0.0):y(ya),x(xa){}
};	

可初始化任何类型的数据,早于构造函数执行

Student::Student(string s):name(s){}  //初始化
Student::Student(string s){name = s;} //初始化加赋值

对象组合
软件重用 fully 成员变量是对象本身/by reference 成员变量是一个指针
对32字节来说,指针就是4个字节

class Person{};
class Currency{};
class SavingAccount {
public:
	SavingAccount(const char* name, const char* address, int cents);
	SavingAccount();
	void print();
private:
	Person m_saver;
	Currency m_balance;
};
SavingAccount::SavingAccount(const char* name, const char* address, int cents)
	:m_saver(name,address),m_balance(0,cents){}
 
void SavingAccount::print() {
	m_saver.print();
	m_balance.print();
}

继承
拿已有的类做改造得到新的类
student具有更多的数据和能力(superset)
基类,超类,父类
派生类,子类
子类可以调用父类的成员函数,也可以增加新的功能
但是不能访问父类私有的东西,只能通过规则(set等去访问)

class A {
public:
	A():i(0) { cout<<"A::A()" << endl; }
	~A() { cout << "A::~A()" << endl; }
	void print() { cout << "A::print()" << i << endl; }
	void set(int ii) { i = ii; }
private:
	int i;
};

class B : public A {   //B是A的子类
public:
	void f() { set(20); i = 30; print(); } //error:不能访问i;
};

int main() {
	B b;
	b.set(10);
	b.print();
	b.f();
	return 0;
}

如果set前加protected,则需要在main函数把b.set去掉
所有数据都是private,给所有人用的放在public,留下protected接口留给子类来访问private数据

子类父类关系
inline内联
重载
若A类构造函数含有传入参数A::A(int ii) {…}
子类B创建时 B b会出现错误,显示无法引用B的默认构造函数
子类体内有父类所有的东西
创建B时会调用A的构造函数,A没有给出缺省构造函数
需要给B一个构造函数
B():A(15){} 通过初始化列表送参数给父类,而不能写在构造函数中,因为构造函数不能主动被调用

class A {
public:
	A(int ii):i(0) { cout<<"A::A()" << endl; }
}class B : public A {   //B是A的子类
public:
	B() :A(15) {};
};

所以 父类的初始化构造也必须放在子类的初始化列表中,如果没有写在初始化列表中,则会调用父类的default constructor;
父类放在最先,其他成员变量按照顺序依次写进去
父类首先被构造,子类再被构造,析构顺序相反

class Employee
{
  public:
    Employee(const std::string& name, const std::string& ssn);
    const std::string& get_name() const;
    void print(std::ostream& out) const;
    void print(std::ostream& out, const std::string& msg) const;
  //让成员变量为protected,好处是子类可以直接调用m_name,m_ssn.
  //                    坏处是不知道子类会拿m_name和m_ssn做什么.
  //建议成员变量用private.
  protected: 
    std::string m_name;
    std::string m_ssn;
};

//构造函数
Employee::Employee(const string& name, const string& ssn):m_name(name),m_ssn(ssn)
{
   //initializer list sets up the values.
}

inline const std::string& Employee::get_name() const
{
   return m_name;
}

inline void Employee::print(std::ostream& out) const
{
   out << m_name << endl;
   out << m_ssn << endl;
}

inline void Employee::print(std::ostream& out, const std::string& msg) const //两个print函数名相同,参数不同,函数重载
{
  out << msg << endl;
  print(out); // 编译器会根据参数的数量类型不同,决定该调用哪一个函数. 
             //调用了第一个print函数,而不是抄写一遍. 尽量利用之前写的代码. 避免代码复制.
}

class Manager:public Employee
{
  public:
    Manager(const std::string& name, const std::string& ssn, const std::string& title);
    const std::string title_name() const;
    const std::string& get_title() const;
    void print(std::ostream& out) const;
  private:
    std::string m_title;
};

//初始化列表
Manager::Manager(const string& name, const string& ssn, const string& title=""):Employee(name,ssn),m_title(title)
{
}

inline void Manager::print(std::ostream& out)
{
  Employee::print(out); //尽量避免代码重写 函数重构时写解析符.
  out << m_title << endl;
}

inline const std::string& Manager::get_title() const
{
  return m_title;
}

inline const std::string Manager::title_name() const
{
  return string(m_title + ": " + m_name);
}

int main()
{
  Employee bob("Bob Jones", "555-44-0000");
   //父类Employee,此时Employee中的bob对象的protected成员变量m_name = "Bob Jones", m_ssn = "555-44-0000" (成员变量是对象的,成员函数是类的)

  Manager bill("Bill Smith", "666-55-1234", "Important Person"); 
  //子类Manager的对象bill中的私有成员变量m_title = "Important Person", 同时m_name = "Bill Smith", m_ssn = "555-44-0000".

  string name = bill.get_name(); 
//子类调用父类的成员函数,name = "Bill Smith".

  //string title = bob.get_title(); //此行出错,不能如此调用,父类不能调用子类的东西.

  cout << bill.title_name() << '\n' << endl;
   // "Important Person : Bill Smith

  bill.print(cout); //"Bob Jones" "555-44-0000"
                    //"Important Person.

  bob.print(cout); //"Bob Jones" "555-44-0000"

  bob.print(cout, "Employee:"); //Employee: "Bob Jones" "555-44-0000"

  //bill.print(cout, "Employee:"); //此行出错

  return 0;
}

父类A和子类B中都有相同函数(重载函数),名字和参数相同,则只剩下子类的函数,父类的被隐藏
调用父类,需要b.A::A.print(200);

函数重载和默认参数值
重载:一些函数具有相同的函数名,但是参数表不同
返回参数不同不能构成重载
参数可以预先给一个值,这样那个参数可给可不给值
default要从最右边开始写
default写在.h中,而在.cpp中不能重复
建议:不要使用default value

内联函数
堆栈中放本地变量和返回地址
将inline函数的body放入.h文件中
加上inline后函数的定义会变成声明
将body直接插入函数处,牺牲代码空间,但是节约调用时间
比C的宏好,宏不能做类型检查
递归不能inline,需要不停进栈,出栈,特别大的也不能
class将body直接写进去也是内联的一种,此时一个类只需要放在.h中就行了
如果不把body直接写进去的话,.h中可以在A class后面加 inline A::set()
循环中可以使用inline

const
变量被初始化赋值后不能再修改

const int x = 123;
x = 27; //wrong
x++; //wrong

int y = x; //ok
y=x;
const int z = y; //ok

const仍是变量,而不是常数,需要分配地址
遵循scope rules
extern:全局变量的声明

const int bufsize = 1024;
extern const int bufsize;//与它本身是否是const无关
const int class_size = 12;
int finalGrade[class_size]; //ok


int x;
cin >> x;
const int size = x;  //error,因为编译器不知道x的值
//编译器需要知道本地变量的大小
同理extern也不行,因为不知道大小

指针变量的const

char * const q = "abc";//q is const
*q = 'c';//ok
q++; //error
const char *p = "ABCD"; //(*p) is a const char
*p = 'b'; //error  不能通过P修改内存单元,而不是说变量本身是const,早在编译时就决定了
Person p1("Fred", 200);
const Person* p = &p1; //*p is const
Person const* p = &p1; //*p is const 对象是const
Person *const p = &p1; //p指针 is const

区分的标志: const*的前面还是后面, const*前面,对象是const
                                 const*后面,指针是const
char *s = "hello,world"; //warning(此时编译器不知道,被骗)提示做法过时
cout<< s << endl;
s[0] = 'B';
cout<< s << endl;//segmant fault /bus error 异常终止

s在堆栈中,是个指针,本地变量,指向了一块内存
new出来的东西放在堆中
全局变量放在全局数据区中,常数“hello,world"放在代码段中,是个const的东西,代码段不可写
内存管理单元 MMU

const  *s = "hello,world";  //再运行提示error
char s[] = "hello,world"; //运行成功

因为整个数组在堆栈中,等号意味着拷贝

void f(const int* x); //虽然你传给我的是指针,但是我保证在里面x不会被修改
int a = 15;
f(&a)=15; //ok
const int b =a;
f(&b);//ok
b = a+1;//error

把const看作一个值
以const的方式传一个对象进去,即是安全的
const A(40) 但这样这个对象无法被修改
可以在函数后面加const

int A::set() const{
 i++;//error
 f(10)//error calls non-const member

原型和body中都需要加上const,
int set () const;
int set() const {…};
相当于说this是const;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值