访问修饰符
-
private
:表示仅能在合约内部访问;可用于函数、状态变量 -
internal
:表示能在合约内部或继承合约内部访问;可用于函数、状态变量 -
public
:表示能在任何地方访问;可用于函数、状态变量 -
external
:表示仅能在合约外部访问;仅可用于函数(但内部可以通过this.f()
来调用)
contract Demo {
uint private privateVar = 1;
uint internalVar = 2; // default internal
uint public publicVar = 3;
function privateFunc() private view returns (uint) {
return privateVar;
}
function internalFunc() internal view returns (uint) {
return internalVar;
}
function publicFunc() public view returns (uint) {
return publicVar;
}
function externalFunc() external pure returns (uint) {
return 4;
}
function testFuncInDemo() public view {
privateVar + internalVar + publicVar;
privateFunc();
internalFunc();
publicFunc();
// externalFunc();
}
}
contract ChildDemo is Demo {
function testFuncInChild() public view {
publicVar + internalVar + publicVar;
// privateFunc();
internalFunc();
publicFunc();
// externalFunc();
}
}
contract ExternalDemo {
Demo demo = new Demo(); // 通过 new 创建外部合约实例
function testFuncInExternal() public view {
demo.publicVar;
// demo.privateFunc();
// demo.internalFunc();
demo.publicFunc();
demo.externalFunc();
}
}
在 Solidity 中,public 修饰的状态变量会自动生成 getter 函数,一般情况下 可以直接访问变量,但也有需要调用 getter 的情况:
contract Demo {
uint public publicVar = 1;
}
contract ExternalDemo {
Demo demo = new Demo(); // 通过 new 创建外部合约实例
function testFunc() public view returns (uint) {
demo.publicVar; // 直接访问
return demo.publicVar(); // 通过 getter 访问
}
}
函数修饰符
pure
:表示函数既不读取也不修改合约的状态变量view
:表示函数会读取合约的状态变量,但不修改它们
contract Demo {
uint public x = 10;
function pureFunc(uint a, uint b) public pure returns (uint) {
return a * b;
}
function viewFunc() public view returns (uint) {
return x;
}
}
对于会修改状态变量的函数,不需要函数修饰符:
contract Demo {
uint public count;
function increment() external {
count++;
}
}
pure 和 view 函数不改写链上状态,用户直接调用这些函数不需要支付 gas 。但需要注意,合约内部非 pure/view 函数调用 pure/view 函数时仍需支付 gas 。
在以太坊中,以下语句被视为修改链上状态:① 写入状态变量; ② 释放事件; ③ 创建其他合约; ④ 使用 self destruct; ⑤ 通过调用发送以太币; ⑥ 调用任何未标记 view 或 pure 的函数; ⑦ 使用低级调用(low-level calls); ⑧ 使用包含某些操作码的内联汇编。
状态变量修饰符
constant
:表示变量会在 [声明时] 确定值,且之后不会再更新;仅可用于值类型、string、bytesimmutable
:表示变量会在 [声明时] / [合约部署时 (优先级更高)] 确定值,且之后不会再更新;仅可用于值类型
contract Demo {
uint public constant NUM = 100;
address public immutable OWNER; // 0.8.21 之前需在声明时初始化
constructor() {
OWNER = msg.sender;
}
}
本地变量修饰符
在函数中,可用 storage、memory、calldata 修饰引用类型数据。消耗的 gas:storage > memory > calldata 。
storage
:表示此处操作的是数据地址;仅可用于变量memory
:表示此处操作的是数据副本;可用于变量、参数、返回值calldata
:表示数据只读,不可操作;仅可用于参数
contract Demo {
struct Person {
string name;
uint age;
}
Person[] public persons;
// calldata: 表示数据只读, 不可操作; 仅可用于参数
function addPerson(string calldata _name, uint _age) public {
persons.push(Person(_name, _age));
}
function getPerson(uint _index) public view returns (string memory, uint) {
// memory: 表示此处操作的是数据副本; 可用于变量、参数、返回值
return (persons[_index].name, persons[_index].age);
}
function updatePerson(uint _index, uint _age) public {
// storage: 表示此处操作的是数据地址; 仅可用于变量
Person storage person = persons[_index];
person.age = _age;
}
}
需要注意的是,memory 赋值 memory 时操作的也是数据地址:
contract Demo {
function memoryCo(
uint[] memory arr
) public pure returns (uint[] memory, uint[] memory) {
uint[] memory newArr = arr;
newArr[0] = 100;
return (newArr, arr); // newArr 和 arr 都被修改了
}
}