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;